call chaining clarified

Walter Bright newshound1 at digitalmars.com
Fri Mar 30 00:08:02 PDT 2007


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.


>> 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.


>> 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.



More information about the Digitalmars-d mailing list