Value closures (no GC allocation)

Adam D. Ruppe via Digitalmars-d digitalmars-d at puremagic.com
Sun May 21 11:17:57 PDT 2017


On Sunday, 21 May 2017 at 04:08:04 UTC, Vittorio Romeo wrote:
> This exact statement applied to C++ before C++11, but the 
> introduction of lambda expression significantly changed the way 
> people write and think about C++. Sometimes syntactic sugar can 
> have huge impact on a language.

Oh absolutely, make no mistake, I would be FOR this addition. I 
like it and do think it would be worth it in a lot of places.

But, using the struct stuff, we can add some artificial sweetener 
now:

return bar(lambda!(x, q{ (int y) => x + y }));

You pass the captures first, then a q{} string literal of the 
lambda. Here's the implementation of that lambda template:



     template lambda(Args...) {
         import std.conv;
         import std.range;
         import std.string;
         string evil() {
                 // build the functor
                 import std.meta;
                 string code = "static struct anon {";
                 foreach(i; aliasSeqOf!(iota(0, Args.length-1)))
                         code ~= "typeof(Args[" ~ to!string(i) ~ 
"]) " ~ Args[i].stringof ~ ";";

                 string func = Args[$-1];
                 auto idx = func.indexOf("=>");
                 if(idx == -1)
                         throw new Exception("No => in lambda"); 
// or we could use one of the other styles

                 auto args = func[0 .. idx];
                 auto bod  = func[idx + 2 .. $];

                 code ~= "auto opCall(T...)" ~ args ~ "{ return " 
~ bod ~ "; }";

                 code ~= "this(T...)(T t) {
                         this.tupleof = t;
                 };";

                 code ~= "}";
                 return code;
         }
         mixin(evil());
         anon lambda() {
                 anon a;
                 // copy the values in
                 a.tupleof = Args[0 .. $-1];
                 return a;
         }
     }





Yes, the C++ syntax is still a bit better and can give MUCH nicer 
error messages, but for short things, this isn't bad.


>     foreach(i; 0..5)
>     {
>         arr ~= () => writeln(i);
>     }

so that's actually a long standing bug, but it hasn't been fixed 
for a long time....

But to work with that, you can do a capture with a wrapper 
function:

      arr ~= ((i) => delegate() { writeln(i); })(i);

So you define a new function that returns the delegate and pass 
the argument right there. This technique is common in Javascript.

Or, of course, using the artificial sweetener above, you can do:

arr ~= lambda!(i, q{ writeln(i); });

...assuming the imports are correct to call that library 
function... so i'll grant the artificial sweetener can leave a 
bitter aftertaste. (This is a good case to use in a feature 
request as to why the string mixin trick isn't actually a great 
replacement!)


More information about the Digitalmars-d mailing list