Pattern for safe function-with-closure delegates

Steve Horne stephenwantshornenospam100 at aol.com
Wed Sep 6 00:47:45 PDT 2006


I understand that delegates used as functions with closures become
invalid when the function enclosing the definition exists. A delegate
that outlives the function where it was created can be a bad thing for
this reason.

The thing is that this is a silent error. There are no compiler errors
or warnings, and there is no exception when the delegate is called. It
just silently uses a dangling pointer - the invalidity of the pointer
can't be detected since it still points into a vaguely valid looking
location (the stack).

As a rule, I have no big problems with this. D is a systems
programming language, there are sometimes trade-offs between
efficiency and safety, and it is down to the programmer to take
responsibility.

BUT - I still want a pattern which will let me detect errors in some
cases, either as debug code or in cases where I have to trust a
delegate to a library which could concievably keep it alive to long.

One possibility is to use an inner (maybe anonymous) class, and return
a delegate to a member function of that. This would give a 'genuine'
closure - copying in the needed variables as members. So long as only
the member copies were used in the delegated member itself, this is
safe - the instance is stored on the heap and garbage collection only
happens when all references (delegate included) are dead.

Trouble is, it is also inefficient - presumably the reason why D
doesn't have genuine closures for inner functions already. A lot of
the time, that extra overhead just isn't needed.

One idea I came up with which works is using a static flag and a scope
statement, roughly as follows (not tested code)...


void Make_Delegate (out int delegate () p_Delegate)
{
  static bool l_Delegate_Dead = true;
  int l_Var = 5;

  //...

  int Delegate ()
  {
    if (l_Delegate_Dead)
      throw new Exception ("Dead delegate called.");

    return l_Var;
  }

  p_Delegate = Delegate;
  l_Delegate_Dead = false;
  scope(exit) l_Delegate_Dead = true;

  //...
}

void Test ()
{
  int delegate () l_Delegate;

  Make_Delegate (l_Delegate);

  Writefln ("Result : ", l_Delegate ());  //  throws exception
}


This seems, to me, just complex enough that if I used it a lot, it
would be error prone in itself. Too easy to forget the check in the
delegate, or to forget the flag setting or the scope statement when
needed.

Does anyone have any thoughts on this?




More information about the Digitalmars-d-learn mailing list