On Sat, 31 Oct 2009 20:52:05 -0400, Adam D. Ruppe
<[email protected]> wrote:
I'm trying to make a delegate thing in a loop and hit something that
seemed intuitively wrong. Here's some short code that shows what I mean:
===
import std.stdio;
class A {
this(void delegate() _a) {
a = _a;
}
void run() { a(); }
private void delegate() a;
}
void main() {
auto list = ['a', 'b', 'c'];
A[string] buttons;
foreach(l; list) {
buttons[l ~ "\n"] = new A( { writefln("%s", l); } );
}
auto a = readln();
buttons[a].run;
}
===
What happens is no matter what key you press, you always get the output
of "c", whereas at first glance, I'd expect it to echo back the same
letter to you. This makes sense when thinking about it in terms of
memory: the delegate accesses the memory that variable l had assigned to
it, which was overwritten by later loop iterations.
But, while it makes sense when thinking about it, intuitively, I
expected that loop variable to be copied over somewhere, so the delegate
would be accessing its own private copy, not the pointer being
overwritten by the loop.
Generally, if a delegate escapes any scope, I expect it to take a
snapshot of the stack it is referencing at that time to make its
private copy at the time it passes the closing brace.
What I'm asking is:
a) Is my expectation wrong, or does that make sense?
b) If not wrong, should D be doing this now, or is this a bug?
and c) If it isn't a bug, should it be? Copying it on every loop
iteration would have a definite performance penalty, so it isn't
remotely free. Moreover, being able to reference variables that might
change later can be likely a good thing:
[code]
SomeClass a;
foreach(item; collection)
item.someDelegate = { a.doSomething;}
a = new SomeClass;
[/code]
If it make a private copy inside that loop, someDelegate would be
looking at null the whole time, whereas with the current behaviour, this
code works.
So changing it might not even be sane from that perspective.
What do you guys think?
For me, this makes sense and isn't a bug. A closure should be able to
mutate it's enclosing scope and be affected by mutation. That said, a
enhancement that allowed variables to copied by value, instead of by
reference, might be nice. As for your particular issue, since it looks
like you're doing GUI stuff, you might want to take a look at DFL. One of
the things it does it to has all it's delegates take an object and an args
object. As most of DFL's objects support tags, this allows you to retrieve
custom information from the delegate arguments (though casting all the
time does get ugly)