The future of lambda delegates

Sean Kelly sean at f4.ca
Thu Aug 17 06:57:53 PDT 2006


pragma wrote:
> Sean Kelly wrote:
>> Walter Bright wrote:
>>> Sean Kelly wrote:
>>>> The alternative would be to use a separate keyword for these 
>>>> delegates, 'closure' or 'lambda' or some such, but that's 
>>>> potentially confusing, and leaves anonymous delegates in an odd 
>>>> position.
>>>
>>> I *really* want to avoid having to do this. It's almost guaranteed 
>>> that it'll be a rich source of bugs.
>>
>> As an alternative, since it's really how the delegate is used that's 
>> at issue, perhaps the programmer could simply be given a way to 
>> manually "archive" the stack frame used by a delegate if he knows it 
>> will need to be called asynchronously?  From the original example:
>>
>>     Button createButton(char[] click_msg)
>>     {
>>         Button b = new Button();
>>         b.mouseClickCallback = { MsgBox(click_msg); };
>>         return b;
>>     }
>>
>> Let's assume mouseClickCallback is written like so:
>>
>>     void mouseClickCallback( void delegate() dg )
>>     {
>>         clickHandler = dg;
>>     }
>>
>> Following my suggestion, it would be changed to this:
>>
>>     void mouseClickCallback( void delegate() dg )
>>     {
>>         dg.archive;
>>         clickHandler = dg;
>>     }
>>
>> The archive routine would check dg's stack frame to see if a heap copy 
>> of the frame exists (assume it's stored as a pointer at this[0]).  If 
>> not then memory is allocated, the pointer is set, the frame is copied, 
>> and dg's 'this' pointer is updated to refer to the dynamic frame. 
>> Returning a delegate from a function would just implicitly call this 
>> 'archive' routine.  This could still cause errors, as a programmer may 
>> forget to call "dg.archive" before storing the delegate, but I think 
>> this is an acceptable risk and is far better than having the compiler 
>> try to "figure out" whether such a dynamic allocation is needed.  It 
>> also seems fairly easy to implement compared to the alternatives, and 
>> offering the feature through a property method would eliminate the 
>> need for a new keyword.
> 
> I haven't read the entire thread yet, but I think something like this 
> might be the right ticket.  Although the '.archive' property might cause 
> collisions with user code on the return type.
> 
> Wouldn't it be better if we were to overload the use of 'new' on 
> delegates/functions to do this instead?
> 
>     void mouseClickCallback( void delegate() dg )
>     {
>         clickHandler = new dg; //
>     }
> 
> ... or do we require the use of a delegate type instead of an instance?
> 
> Either way, as 'new' is implied to do heap allocation with classes, 
> we're now doing the same on a given delegate for it's frame.

I do like this idea the best, but here are some of the problems that 
occurred to me:

     void setCallback( void delegate() dg )
     {
         callback = new dg;
     }

     void evilFn1()
     {
         int callCount = 0;

         void dg()
         {
             callCount++;
         }

         setCallback( &dg );

         while( true )
         {
             Thread.sleep( 1000 );
             printf( "Called %d times.\n", callCount );
         }
     }

     void evilFn2()
     {
         int[10] buf;
         int* cur = &buf[0];

         void dg()
         {
             printf( "%d\n", *cur );
             if( ++cur > &buf[9] )
                 cur = &buf[0];
         }

         initBuf( buf );
         setCallback( &dg );
     }

Since my original proposal was to create a dynamic copy of the stack 
frame for delegates as needed after the original stack frame had been 
created, it assumed that the original stack frame would be gone or 
irrelevant when the delegate was called.  In evilFn1, such behavior 
would result in "Called 0 times." being printed regardless of the number 
of times the delegate is called.  evilFn2 demonstrates that a memcpy of 
the stack frame may not be sufficient to preserve intended behavior.  I 
do think both of these could probably be overcome with fancier code 
generation, but they would make the code complex and somewhat 
non-obvious to a debugger.  I still like this idea the best because it 
allows the programmer aware that the frame must be preserved to do so, 
but someone would have to resolve the above problems to make it correct 
and safe.  And I didn't even mention the problems with passing a 
delegate from a struct on the stack or a class constructed in-place on 
the stack with alloca.


Sean



More information about the Digitalmars-d mailing list