February 27, 2003
"Jeroen van Bemmel" <anonymous@somewhere.com> wrote in message news:b3kh99$qoj$1@digitaldaemon.com...
>
> The advantage is though that I can return this object from my function,
and
> use it elsewhere. What happens if I return a reference to a nested
function
> delegate in D? Is it still valid, is there also some memory allocated underneath? You would expect it to go out of scope, so the compiler should issue a warning "returning reference to local function" or something
>
>

That's the rub.  The nested function has static scope, so it will always be accesible and callable. It's enclosing environment is the problem. Local variables on the stack in its enclosing environment will become invalid when the surrounding function exits, so it's caller beware.

An example where the D version will get you (by letting you introduce a
subtle runtime bug) is this (shamelessly stolen from the Vault page):

void startLogging (string logFilename) {
  file_handle logfile = openFile(logFilename, "a");
  void handleInterrupt (int interrupt) {
    writeMessage(interrupt, logfile);
  }
  registerInterruptHandler(12, handleInterrupt);
}

When "handleInterrupt" is called later, "startLogging" will have exited, and "logfile" will have fallen out of scope. It is a local stack variable, so it will point to what is likely to be invalid memory. Cue access violation or worse.

Dan


February 27, 2003
"Jeroen van Bemmel" <anonymous@somewhere.com> wrote in message news:b3kh99$qoj$1@digitaldaemon.com...
> BTW, now that I'm thinking about this: One of the worse features of Java
IMO
> is the rediculous amount of resources (memory) it uses for simple things. The garbage collector is good, but it allocates so many small objects (the example I gave allocates one for the function object).

True, one thing I was going to point out about your example is that the D way of doing it is far more efficient of run time and memory.

> The advantage is though that I can return this object from my function,
and
> use it elsewhere. What happens if I return a reference to a nested
function
> delegate in D? Is it still valid, is there also some memory allocated underneath? You would expect it to go out of scope, so the compiler should issue a warning "returning reference to local function" or something

No memory is allocated underneath, a hidden pointer to the stack frame is just saved and passed as a parameter to f(). That means, if the function goes out of scope, it is just as much an error as returning a pointer to a local:

    int *wrong()
    { int i;
       return &i;
    }


February 27, 2003
"Dan Liebgold" <dliebgold@yahoo.com> wrote in message news:b3kj07$rnc$1@digitaldaemon.com...
> That's the rub.  The nested function has static scope, so it will always
be
> accesible and callable. It's enclosing environment is the problem. Local variables on the stack in its enclosing environment will become invalid
when
> the surrounding function exits, so it's caller beware.
>
> An example where the D version will get you (by letting you introduce a
> subtle runtime bug) is this (shamelessly stolen from the Vault page):
>
> void startLogging (string logFilename) {
>   file_handle logfile = openFile(logFilename, "a");
>   void handleInterrupt (int interrupt) {
>     writeMessage(interrupt, logfile);
>   }
>   registerInterruptHandler(12, handleInterrupt);
> }
>
> When "handleInterrupt" is called later, "startLogging" will have exited,
and
> "logfile" will have fallen out of scope. It is a local stack variable, so
it
> will point to what is likely to be invalid memory. Cue access violation or worse.

Yup, you're right, that will fail in D. The way to make it work is to make handleInterrupt a member of a class:

void startLogging(string logFilename)
{
    class Foo
    {    file_handle logfile;
         void handleInterrupt(int interrupt) { writeMessage(interrupt,
logfile); }
    }
    Foo f = new Foo;
    f.logfile = openFile(logFilename, "a");
    registerInterruptHandler(12, f.handleInterrupt);
}

It's a little more work, but not so bad.


February 27, 2003
>
> It's a little more work, but not so bad.
>

The point is not that you wouldn't be able to make it work, the point is that you create a bug that is hard to find. This kind of use of nested functions should be detected and refused. It should be illegal to store a reference to a nested local function. In fact, the type of a nested function should not be 'delegate' but "non_copyable_delegate' - can you make this ?


February 27, 2003
"Jeroen van Bemmel" <anonymous@somewhere.com> wrote in message news:b3ligi$1gno$1@digitaldaemon.com...
> The point is not that you wouldn't be able to make it work, the point is that you create a bug that is hard to find. This kind of use of nested functions should be detected and refused. It should be illegal to store a reference to a nested local function. In fact, the type of a nested
function
> should not be 'delegate' but "non_copyable_delegate' - can you make this ?

I agree that as much as possible the compiler should detect and reject such usage as illegal. But it cannot do it 100%. Another possibility would be to add a runtime check.


February 27, 2003
In article <b3ko0o$ueh$1@digitaldaemon.com>, Walter says...
>
>Yup, you're right, that will fail in D. The way to make it work is to make handleInterrupt a member of a class:
>
>void startLogging(string logFilename)
>{
>    class Foo
>    {    file_handle logfile;
>         void handleInterrupt(int interrupt) { writeMessage(interrupt,
>logfile); }
>    }
>    Foo f = new Foo;
>    f.logfile = openFile(logFilename, "a");
>    registerInterruptHandler(12, f.handleInterrupt);
>}
>
>It's a little more work, but not so bad.
>
>

Ah.. clever approach, certainly.  Do-it-yourself lexical closures, similar to the Java approach.

I'm afraid I agree with Jeroen on this point though, it is very easy to introduce that subtle bug of referencing de-scoped locals.  Also, in the above example, won't f get garbage collected soon?   Does the collector need to track references to f's members for this construct to work? (Does it anyway? I'm not familiar with the techniques it uses...)

Dan


February 27, 2003
In article <b3lri6$1m8f$1@digitaldaemon.com>, Dan Liebgold says...
>
>In article <b3ko0o$ueh$1@digitaldaemon.com>, Walter says...
>>
>>Yup, you're right, that will fail in D. The way to make it work is to make handleInterrupt a member of a class:
>>
>>void startLogging(string logFilename)
>>{
>>    class Foo
>>    {    file_handle logfile;
>>         void handleInterrupt(int interrupt) { writeMessage(interrupt,
>>logfile); }
>>    }
>>    Foo f = new Foo;
>>    f.logfile = openFile(logFilename, "a");
>>    registerInterruptHandler(12, f.handleInterrupt);
>>}
>>
>>It's a little more work, but not so bad.
>>
>>
>
>Ah.. clever approach, certainly.  Do-it-yourself lexical closures, similar to the Java approach.
>
>I'm afraid I agree with Jeroen on this point though, it is very easy to introduce that subtle bug of referencing de-scoped locals.  Also, in the above example, won't f get garbage collected soon?   Does the collector need to track references to f's members for this construct to work? (Does it anyway? I'm not familiar with the techniques it uses...)
>
>Dan

No the pointer will be held in a delegate which is a object/function pointer pair.  Since there is an active pointer to the object it won't be collected.


February 27, 2003
"Dan Liebgold" <Dan_member@pathlink.com> wrote in message news:b3lri6$1m8f$1@digitaldaemon.com...
> I'm afraid I agree with Jeroen on this point though, it is very easy to introduce that subtle bug of referencing de-scoped locals.

I'm thinking it may be possible to add a runtime check for that.

> Also, in the above
> example, won't f get garbage collected soon?

No.

> Does the collector need to track
> references to f's members for this construct to work? (Does it anyway? I'm
not
> familiar with the techniques it uses...)

It scans the entire stack for references. f.handleInterrupt will contain a reference to f, and the usual places that the delegate will be stored will be scanned.


February 28, 2003
> I'm thinking it may be possible to add a runtime check for that.

It may even be possible to do static checking:

the 'registerInterruptHandler(12, handleInterrupt)' in the example would have to be declared as registerInterruptHandler( int, global delegate()) to be allowed to store the reference to the delegate somewhere. Passing 'handleInterrupt' would then be disallowed, since nested functions are of type "local delegate" and thus not assignment compatible

You could discuss about the wording (local vs global or something else) or
perhaps define a default ( all 'delegate' parameter values are assumed local
(not storable) unless explicitly declared "global" (or "storable"), or vice
versa )



February 28, 2003
"Jeroen van Bemmel" <anonymous@somewhere.com> wrote in message news:b3mab5$1vde$1@digitaldaemon.com...
> > I'm thinking it may be possible to add a runtime check for that.
>
> It may even be possible to do static checking:
>
> the 'registerInterruptHandler(12, handleInterrupt)' in the example would
> have to be declared as registerInterruptHandler( int, global delegate())
to
> be allowed to store the reference to the delegate somewhere. Passing 'handleInterrupt' would then be disallowed, since nested functions are of type "local delegate" and thus not assignment compatible
>
> You could discuss about the wording (local vs global or something else) or
> perhaps define a default ( all 'delegate' parameter values are assumed
local
> (not storable) unless explicitly declared "global" (or "storable"), or
vice
> versa )

Some good thoughts there.