www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - lambda function with "capture by value"

reply Simon =?UTF-8?B?QsO8cmdlcg==?= <simon.buerger rwth-aachen.de> writes:
If a lambda function uses a local variable, that variable is 
captured using a hidden this-pointer. But this capturing is 
always by reference. Example:

     int i = 1;
     auto dg = (){ writefln("%s", i); };
     i = 2;
     dg(); // prints '2'

Is there a way to make the delegate "capture by value" so that 
the call prints '1'?

Note that in C++, both variants are available using
    [&]() { printf("%d", i); }
and
    [=]() { printf("%d", i); }
respectively.
Aug 05
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Saturday, 5 August 2017 at 18:17:49 UTC, Simon Bürger wrote:
 If a lambda function uses a local variable, that variable is 
 captured using a hidden this-pointer. But this capturing is 
 always by reference. Example:

     int i = 1;
     auto dg = (){ writefln("%s", i); };
     i = 2;
     dg(); // prints '2'

 Is there a way to make the delegate "capture by value" so that 
 the call prints '1'?

 Note that in C++, both variants are available using
    [&]() { printf("%d", i); }
 and
    [=]() { printf("%d", i); }
 respectively.
No currently there is not.
Aug 05
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Saturday, 5 August 2017 at 18:19:05 UTC, Stefan Koch wrote:
 On Saturday, 5 August 2017 at 18:17:49 UTC, Simon Bürger wrote:
 If a lambda function uses a local variable, that variable is 
 captured using a hidden this-pointer. But this capturing is 
 always by reference. Example:

     int i = 1;
     auto dg = (){ writefln("%s", i); };
     i = 2;
     dg(); // prints '2'

 Is there a way to make the delegate "capture by value" so that 
 the call prints '1'?

 Note that in C++, both variants are available using
   [&]() { printf("%d", i); }
 and
    [=]() { printf("%d", i); }
 respectively.
No currently there is not.
and it'd be rather useless I guess. You want i to be whatever the context i is a the point where you call the delegate. Not at the point where you define the delegate.
Aug 05
parent reply Simon =?UTF-8?B?QsO8cmdlcg==?= <simon.buerger rwth-aachen.de> writes:
On Saturday, 5 August 2017 at 18:22:38 UTC, Stefan Koch wrote:
 On Saturday, 5 August 2017 at 18:19:05 UTC, Stefan Koch wrote:
 On Saturday, 5 August 2017 at 18:17:49 UTC, Simon Bürger wrote:
 If a lambda function uses a local variable, that variable is 
 captured using a hidden this-pointer. But this capturing is 
 always by reference. Example:

     int i = 1;
     auto dg = (){ writefln("%s", i); };
     i = 2;
     dg(); // prints '2'

 Is there a way to make the delegate "capture by value" so 
 that the call prints '1'?

 Note that in C++, both variants are available using
   [&]() { printf("%d", i); }
 and
    [=]() { printf("%d", i); }
 respectively.
No currently there is not.
and it'd be rather useless I guess. You want i to be whatever the context i is a the point where you call the delegate. Not at the point where you define the delegate.
No, sometimes I want i to be the value it has at the time the delegate was defined. My actual usecase was more like this: void delegate()[3] dgs; for(int i = 0; i < 3; ++i) dgs[i] = (){writefln("%s", i); }; And I want three different delegates, not three times the same. I tried the following: void delegate()[3] dgs; for(int i = 0; i < 3; ++i) { int j = i; dgs[i] = (){writefln("%s", j); }; } I thought that 'j' should be considered a new variable each time around, but sadly it doesn't work.
Aug 05
parent reply ikod <geller.garry gmail.com> writes:
On Saturday, 5 August 2017 at 18:45:34 UTC, Simon Bürger wrote:
 On Saturday, 5 August 2017 at 18:22:38 UTC, Stefan Koch wrote:
 [...]
No, sometimes I want i to be the value it has at the time the delegate was defined. My actual usecase was more like this: void delegate()[3] dgs; for(int i = 0; i < 3; ++i) dgs[i] = (){writefln("%s", i); }; And I want three different delegates, not three times the same. I tried the following: void delegate()[3] dgs; for(int i = 0; i < 3; ++i) { int j = i; dgs[i] = (){writefln("%s", j); }; } I thought that 'j' should be considered a new variable each time around, but sadly it doesn't work.
Maybe std.functional.partial can help you.
Aug 05
next sibling parent Simon =?UTF-8?B?QsO8cmdlcg==?= <simon.buerger rwth-aachen.de> writes:
On Saturday, 5 August 2017 at 18:54:22 UTC, ikod wrote:
 On Saturday, 5 August 2017 at 18:45:34 UTC, Simon Bürger wrote:
 On Saturday, 5 August 2017 at 18:22:38 UTC, Stefan Koch wrote:
 [...]
No, sometimes I want i to be the value it has at the time the delegate was defined. My actual usecase was more like this: void delegate()[3] dgs; for(int i = 0; i < 3; ++i) dgs[i] = (){writefln("%s", i); }; And I want three different delegates, not three times the same. I tried the following: void delegate()[3] dgs; for(int i = 0; i < 3; ++i) { int j = i; dgs[i] = (){writefln("%s", j); }; } I thought that 'j' should be considered a new variable each time around, but sadly it doesn't work.
Maybe std.functional.partial can help you.
Thanks. But std.functional.partial takes the fixed arguments as template parameters, so they must be known at compile-time. Anyway, I solved my problem already a while ago by replacing delegates with custom structures which overload the call-operator. I opened this thread just out of curiosity. Takes a couple lines more but works fine.
Aug 05
prev sibling parent reply Simon =?UTF-8?B?QsO8cmdlcg==?= <simon.buerger rwth-aachen.de> writes:
On Saturday, 5 August 2017 at 18:54:22 UTC, ikod wrote:
 Maybe std.functional.partial can help you.
Nope. int i = 1; alias dg = partial!(writeln, i); i = 2; dg(); still prints '2' as it should because 'partial' takes 'i' as a symbol, which is - for this purpose - kinda like "by reference". Anyway, I solved my problem already a while ago by replacing delegates with custom struct's that implement the call-operator. I started this thread just out of curiosity, because as I see it, the purpose of lambdas is pretty much to remove the need for such custom constructions.
Aug 05
parent reply Temtaime <temtaime gmail.com> writes:
On Saturday, 5 August 2017 at 19:19:06 UTC, Simon Bürger wrote:
 On Saturday, 5 August 2017 at 18:54:22 UTC, ikod wrote:
 Maybe std.functional.partial can help you.
Nope. int i = 1; alias dg = partial!(writeln, i); i = 2; dg(); still prints '2' as it should because 'partial' takes 'i' as a symbol, which is - for this purpose - kinda like "by reference". Anyway, I solved my problem already a while ago by replacing delegates with custom struct's that implement the call-operator. I started this thread just out of curiosity, because as I see it, the purpose of lambdas is pretty much to remove the need for such custom constructions.
This one works void delegate()[3] dgs; for(int i = 0; i < 3; ++i) { (k){ dgs[k] = {writefln("%s", k); }; }(i); } dgs.each!(a => a());
Aug 05
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 5 August 2017 at 19:58:08 UTC, Temtaime wrote:
  		(k){ dgs[k] = {writefln("%s", k); }; }(i);
Yeah, that's how I'd do it - make a function taking arguments by value that return the delegate you actually want to store. (Also use this pattern in Javascript btw for its `var`, though JS now has `let` which works without this trick... and D is supposed to work like JS `let` it is just buggy). You could also define a struct with members for the values you want, populate it, and pass one of its methods as your delegate. It is syntactically the heaviest but does give the most precise control (and you can pass the struct itself by value to avoid the memory allocation entirely if you want). But for the loop, the pattern Temtaime wrote is how I'd prolly do it.
Aug 06
next sibling parent Russel Winder via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Sun, 2017-08-06 at 12:50 +0000, Adam D. Ruppe via Digitalmars-d-learn
wrote:
 On Saturday, 5 August 2017 at 19:58:08 UTC, Temtaime wrote:
 =C2=A0		(k){ dgs[k] =3D {writefln("%s", k); }; }(i);
=20 Yeah, that's how I'd do it - make a function taking arguments by=C2=A0 value that return the delegate you actually want to store. (Also=C2=A0 use this pattern in Javascript btw for its `var`, though JS now=C2=A0 has `let` which works without this trick... and D is supposed to=C2=A0 work like JS `let` it is just buggy). =20
Assuming I have understood the problem, this is the solution necessary in Python: you have to use an enclosing function around the function so as to capture the value using a parameter. I think Java requires the same. --=20 Russel. =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.winder ekiga.n= et 41 Buckmaster Road m: +44 7770 465 077 xmpp: russel winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder
Aug 06
prev sibling parent Simon =?UTF-8?B?QsO8cmdlcg==?= <simon.buerger rwth-aachen.de> writes:
On Sunday, 6 August 2017 at 12:50:22 UTC, Adam D. Ruppe wrote:
 On Saturday, 5 August 2017 at 19:58:08 UTC, Temtaime wrote:
  		(k){ dgs[k] = {writefln("%s", k); }; }(i);
Yeah, that's how I'd do it - make a function taking arguments by value that return the delegate you actually want to store. (Also use this pattern in Javascript btw for its `var`, though JS now has `let` which works without this trick... and D is supposed to work like JS `let` it is just buggy). You could also define a struct with members for the values you want, populate it, and pass one of its methods as your delegate. It is syntactically the heaviest but does give the most precise control (and you can pass the struct itself by value to avoid the memory allocation entirely if you want). But for the loop, the pattern Temtaime wrote is how I'd prolly do it.
I like the (kinda cryptic IMO) look of this '(k){...}(i)' construction. But for my actual code I went with struct+opCall without any delegate at all. Anyway, thanks for all your suggestions.
Aug 06
prev sibling next sibling parent reply Johnson Jones <JJ Dynomite.com> writes:
On Saturday, 5 August 2017 at 18:17:49 UTC, Simon Bürger wrote:
 If a lambda function uses a local variable, that variable is 
 captured using a hidden this-pointer. But this capturing is 
 always by reference. Example:

     int i = 1;
     auto dg = (){ writefln("%s", i); };
     i = 2;
     dg(); // prints '2'

 Is there a way to make the delegate "capture by value" so that 
 the call prints '1'?

 Note that in C++, both variants are available using
    [&]() { printf("%d", i); }
 and
    [=]() { printf("%d", i); }
 respectively.
There is, but it isn't pretty. import std.stdio; void main() { int i = 1; int* n = null; auto dg = (){ if (n is null) n = cast(int*)i; else writefln("%s", n); }; dg(); i = 2; dg(); // prints '1' } 1. I'm pretty sure that D creates the delegate "lazily" in the sense that the first call is what captures the variable. Hence, we must call it where we want to capture, not after the change occurs. 2. We use a temp local variable to act as a place holder. A singleton basically. You might be able to wrap this up in some type of template that makes it easier to use but it does work.
Aug 05
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 5 August 2017 at 18:37:31 UTC, Johnson Jones wrote:
 1. I'm pretty sure that D creates the delegate "lazily" in the 
 sense that the first call is what captures the variable.
It actually does it at function entry, allocating the memory for the locals in the closure, so it never actually copies them.
Aug 06
prev sibling parent bitwise <bitwise.pvt gmail.com> writes:
On Saturday, 5 August 2017 at 18:17:49 UTC, Simon Bürger wrote:
 If a lambda function uses a local variable, that variable is 
 captured using a hidden this-pointer. But this capturing is 
 always by reference. Example:

     int i = 1;
     auto dg = (){ writefln("%s", i); };
     i = 2;
     dg(); // prints '2'

 Is there a way to make the delegate "capture by value" so that 
 the call prints '1'?

 Note that in C++, both variants are available using
    [&]() { printf("%d", i); }
 and
    [=]() { printf("%d", i); }
 respectively.
I asked about this a couple of day ago: http://forum.dlang.org/thread/ckkswkkvhfojbcczijim forum.dlang.org The problem is that the lambda captures the entire enclosing stack frame. This is actually a bug because the lambda should only capture the enclosing *scope*, not the entire stack frame of the function. So even if you were to copy `i` into a temporary in some nested scope where a lambda was declared (this works in C# for example), that temporary would still reside in the same stack frame as the outer `i`, which means there would still be only one copy of it. There is a workaround in Timon's post here: http://forum.dlang.org/post/om2aqp$2e9t$1 digitalmars.com Basically, that workaround wraps the nested scope in another lambda to force the creation of a separate stack frame.
Aug 06