call chaining clarified

kris foo at bar.com
Fri Mar 30 00:26:00 PDT 2007


Walter Bright wrote:
> kris wrote:
> 
>> Walter Bright wrote:
>>
>>> kris wrote:
>>>
>>>>    Cout.opCall("Hello, ").opCall(Cin.get);
>>>
>>>
>>>
>>> Given:
>>>     a.f(b,c);
>>> can be equivalently written as:
>>>     f(a,b,c);
>>>
>>> we can transform:
>>>     Cout.opCall("Hello, ").opCall(Cin.get);
>>> into:
>>>     opCall(opCall(Cout,"Hello, "), Cin.get);
>>>
>>> And it is transformed that way, because the optimizer/back end knows 
>>> nothing about member functions. They're just more function calls.
>>>
>>> The nested opCall will get called before the outer opCall, but 
>>> otherwise the order is undefined (and will change depending on the 
>>> function calling convention, whether it is a virtual call or not, etc.).
>>
>>
>> Thank you for that clarification, Walter.
>>
>> You note that the inner opCall will be evaluated before the outer one; 
>> how is x.one(a).two(b).three(c) tranformed?
> 
> 
> three(two(one(x,a),b),c)
> 
> The order of evaluation of x, a, b, and c is undefined, even though one 
> is always called before two, and two is always called before three.

Thank you for putting that to rest. Actually, thank you very much.


>>> I suggest that at a minimum, if you wish to retain the current 
>>> design, build a test into the test suite that verifies the required 
>>> order of evaluation.
>>>
>>> It's possible that in the future, to ensure source code portability 
>>> of D, that the order of evaluation will become fixed. Such a change 
>>> is a fair amount of work to accomplish, and will incur a runtime 
>>> penalty (the implementation defined order allows the compiler to 
>>> rearrange things to minimize register pressure). Even if the order 
>>> was fixed, it still might not be in the right order for call chaining 
>>> to work as your design needs it to.
>>
>>
>> Do you mean in terms of generic call-chaining, or that example 
>> specifically? For example, will the code x.one().two().three() always 
>> execute one() before two() ?
> 
> 
> Yes, that is equivalent to: three(two(one(x))). The problem comes when 
> there is more than one argument.

No problemo


>>> I also suggest that, with the maturing of the variadic template 
>>> capability of D, using it will side step the order of evaluation 
>>> problem completely. It'll still be an aesthetically pleasing design 
>>> to the user.
>>
>>
>> That's certainly a possibility, Walter, but call-chaining is surely 
>> expected to operate in an orderly fashion regardless of whether it is 
>> used as an IO interface or not? For example, what happens if two() 
>> were to return a different object for three() to be applied against? 
>> It would surely be incorrect to generate code whereby three() were 
>> called against x instead? In other words, for call chaining to operate 
>> correctly the following pseudocode should likely hold true:
>>
>> # auto result = x.one().two().three();
>>
>> auto tmp1 = x.one();
>> auto tmp2 = tmp1.two();
>> auto result = tmp2.three();
>>
>> Is that incorrect?
> 
> 
> Yes. The problem is when there are more than one arguments involved.

Again, thanks for this clarification. Minor suggestion: perhaps some of 
this might wind up in the docs? For instance, maybe a brief section on 
call-chaining could act as a useful vehicle to clarify these aspects?

~ Kris



More information about the Digitalmars-d mailing list