Trouble with anon delegates.

Pragma ericanderton at yahoo.removeme.com
Tue Jan 16 14:46:44 PST 2007


BCS wrote:
> Reply to Pragma,
> 
>> All,
>> I ran into a small problem regarding the use of anon delegates, and
>> what their context (.ptr) is set to.  If anyone
>> can share any insight as to if this is a bug or not, I would
>> appreciate it.
>> In short: I'm attempting to create delegates on the fly and use them
>> elsewhere.  After many strange errors, I narrowed it down to the
>> following case.  Note the assertions at the end:
>>
>> abstract class Foobar{
>> Fn getFn();
>> }
>> alias void delegate() Fn;
>>
>> auto foo = new class Foobar{
>> int x;
>> Fn getFn(){
>> return { x = x };
>> }
>> };
>> auto fn = foo.getFn();
>> assert((&foo.getFn).ptr is foo); // passes
>> assert(fn.ptr is foo); // fails (ptr does *not* point to the object)
>> So by this, anon delegates are not the same as actual object
>> delegates.  Is this a bug, or is this intentional?
>>
>> "When comparing with nested functions, the function form is analogous
>> to static or non-nested functions, and the delegate form is analogous
>> to non-static nested functions. In other words, a delegate literal can
>> access stack variables in its enclosing function, a function literal
>> cannot."
>>
>> As you can see, the docs are as clear as mud.  I suppose a "non-static
>> nested function" wouldn't know about the 'this' pointer, nor care -
>> but then why encumber it with the 'delegate' keyword?
>>
> 
> 
> Really, some BIG RED TEXT needs to be added to the doc in  few places. 
> This is one of them. Anon delegates are totally invalid after the 
> function they are declared within returns. Any statement of the form:
> 
> return {...};
> 
> is ALWAYS invalid. This has gotten about a dozen people in the last week 
> or so and, as you said. "the docs are as clear as mud" on this. Anon 
> delegate in any scope use the current function's frame pointer as the 
> context. Once the function returns, this more than likely is junk.
> 
> 

Thanks for the clarification.  I wanted to be sure that I wasn't out of my mind.

This is really disappointing.  I was working on a runtime Spirit engine (as a counterpart to your own library) that 
would use inline delegates as valid parse expressions:

auto rule = ws >> '(' >> +Argument >> ')' >> { writefln("args parsed"); };

It works fine for this kind of stuff, but once you graduate beyond there and want to bind terminals to class members, 
you need the ability to perform filtering and aritmetic on them.  But once you do that, you find that your stack frame 
is all out of whack. :(

// This construct is used to help create a context for binding and manipulating parse terms.
// create() is called within LambdaExpr's constructor to create and cache the Expr to be used
// at this stage of the parser.  Of course, the inline delegates are invalid once this returns.

/** 'factor' rule from the infamous 'infix calculator' BNF grammar. */
auto factor = new class LambdaExpr{
     real result,term;
     Expr create(){
         auto posFactor   = T('+') >> ws >> term[&term] >> { result += term; };
         auto negFactor   = T('-') >> ws >> term[&term] >> { result +- term; };
         return             ws >> term[&result] >> -+(ws >> (posFactor | negFactor)) >> { return result; };
     }
};

It would be nice if things so succinct would work as I expected.  Instead I have to break all the delegates out into 
proper class methods, which bloats the code and runs contrary to the whole point of having a parser framework like this.


The ability to declare *first class* delegates as anonymous and inline like the above is just too tempting to leave 
alone.  I suppose the only way to have your cake and eat it too is to preserve the stack somehow.  Maybe this is a job 
for stack threads?

-- 
- EricAnderton at yahoo


More information about the Digitalmars-d-learn mailing list