1 ///
2 module rxd.meta2.closure;
3 
4 import rxd.meta2.traits : isEnumValue;
5 
6 struct Closure(Args...)
7 {
8     pragma (msg, "In Closure template: ", Args.stringof);
9 
10     enum gennedCode = genArgsAliases!Args("Args");
11     pragma (msg, gennedCode);
12     mixin (gennedCode);
13 
14     alias use = typeof(this);
15     private alias __my__args__ = Args;
16 }
17 
18 ///
19 unittest
20 {
21     struct Point { float x, y; }
22 
23     int a = 34;
24     Point b = { 3, 4 };
25     int c = 32;
26     enum d = 42;
27 
28     auto inner = Closure!(c, d)();
29     auto outer = Closure!(inner, a, b)();
30 
31     static assert (inner.sizeof >= (void*).sizeof);
32     static assert (outer.sizeof >= (void*).sizeof);
33 
34     assert (inner.c == 32);
35     assert (outer.a == 34);
36     assert (outer.b == Point(3, 4));
37     assert (outer.c == 32);
38 
39     outer.a++;
40     assert (a == 35);
41     assert (outer.a == 35);
42 
43     outer.c++;
44     assert (c == 33);
45     assert (inner.c == 33);
46     assert (outer.c == 33);
47 
48     void someFunc(C)(C closure)
49     {
50         assert (closure.a == 35);
51 
52         with (closure)
53             assert (a == 35);
54 
55         int local = 85;
56         auto ctx = Closure!(closure, local)();
57 
58         assert (ctx.local == 85);
59         assert (ctx.a == 35);
60     }
61 
62     someFunc(outer);
63 }
64 
65 private string genArgsAliases(Args...)(string prefix = "Args")
66 {
67     //enum string prefix = "Args";
68 
69     import std.conv : to;
70     import std.format : format;
71     import std.typecons : staticIota;
72     string res;
73 
74     pragma (msg, "In genArgsAliases -> ", Args.stringof);
75 
76     string genAliasFunc(string name, string start, size_t idx)
77     {
78         //pragma (msg, "In genAliasFunc -> name: ", name, " start: ", start,
79         //        " idx: ", idx);
80 
81         return ("    auto ref __get__%1$s() { return %2$s[%3$s]; }\n" ~
82                 "    alias %1$s = __get__%1$s;\n")
83             .format(name, start, idx);
84     }
85 
86     foreach (idx; staticIota!(0, Args.length))
87         static if (isEnumValue!(Args[idx]))
88             res ~= "    enum %s = %s[%s];\n"
89                 .format(__traits(identifier, Args[idx]), prefix, idx);
90 
91         else static if (is(typeof(Args[idx]) : Closure!U, U...))
92             res ~= genArgsAliases!(Args[idx].__my__args__)(
93                 "%s[%s].__my__args__".format(prefix, idx));
94 
95         else
96             res ~= genAliasFunc(Args[idx].stringof, prefix, idx);
97 
98     return res;
99 }