call chaining clarified

kris foo at bar.com
Thu Mar 29 23:14:09 PDT 2007


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?


> 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() ?


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



More information about the Digitalmars-d mailing list