1 ///
2 module rxd.meta2.traits;
3 
4 enum isEnumValue(T...) = T.length == 1 &&
5     __traits(compiles, { enum x = T[0]; });
6 
7 ///
8 //enum allConvertible(alias from, alias to) =
9 //    allConvertible!(typeof(from), typeof(to));
10 
11 ///
12 template allConvertible(From, To)
13 {
14     import rxd.meta2.type : isType, isAliasTuple;
15     static if (!isType!From && !isAliasTuple!From &&
16                !isType!To   && !isAliasTuple!To)
17         enum allConvertible = is(From : To);
18 
19     else static if (isType!From)
20         enum allConvertible = allConvertible!(From.typeOf, To);
21 
22     else static if (isType!To)
23         enum allConvertible = allConvertible!(From, To.typeOf);
24 
25     else static if (isAliasTuple!From && isAliasTuple!To)
26     {
27         static if (From.typeTypeSeq.length != To.typeTypeSeq.length)
28             enum allConvertible = false;
29 
30         else static if (From.typeTypeSeq.length == 0)
31             enum allConvertible = true;
32 
33         else
34             enum allConvertible =
35                 allConvertible!(
36                         From.init.front.typeOf,
37                         To.init.front.typeOf
38                 ) &&
39                 allConvertible!(
40                         typeof(From.init.dropOne()),
41                         typeof(To.init.dropOne())
42                 );
43     }
44 
45     else
46         static assert (0, "Unsupported types: " ~ From.stringof ~ " -> " ~
47                 To.stringof);
48 
49 }
50 
51 import std.meta : Alias, AliasSeq;
52 
53 /**
54  * Applies the sequence of arguments `args` to the function or template `fun`
55  * either as function or template arguments, at compile-time or at run-time
56  * (if CTFE is not possible).
57  *
58  * `apply` is most likely to be found used as a building block in higher-order
59  * templates and $(LREF AliasSeq) algorithms like $(LREF staticMap) which need
60  * to call or instantiate (as appropriate) functions or templates, passed as
61  * parameters.
62  *
63  * Params:
64  *      fun  = function or template to call or instantiate
65  *      args = template / function arguments to `apply` to `fun`
66  *
67  * Returns:
68  *      An alias to `fun(args)` or `fun!(args)`
69  */
70 template apply(alias func, args...)
71 {
72     static if (__traits(compiles, { alias res = Alias!(func(args)); }))
73         // Evaluate `fun(args)` at compile-time and wrap the result
74         // in an `Alias`, so it can be used without special-casing
75         // in higher-order templates like `staticMap`.
76         alias apply = Alias!(func(args));
77 
78     else static if (is(typeof(func(args))))
79         // `fun` is a regular callable, but we can't call it at compile-time.
80         // Delay the evaluation of `fun(args)` to run-time by wrapping it in a
81         // `@property` function.
82         @property auto ref apply() { return func(args); }
83 
84     else static if (__traits(compiles, { alias res = Alias!(func!args); }))
85         // `fun` is a manifest constant (a.k.a. `enum`) template or a
86         // function template with no run-time parameters which can be evaulated
87         // at compile-time. Wrap the result in an `Alias`, so it can be aliased.
88         alias apply = Alias!(func!args);
89 
90     else static if (__traits(compiles, { alias res = func!args; }))
91         // `fun` is template that yields an `AliasSeq`, or something that can't
92         // be wrapped with `Alias`.
93         alias apply = func!args;
94 
95     else
96         static assert (0, "Cannot call / instantiate `" ~
97             __traits(identifier, func) ~ "` with arguments: `" ~
98             args.stringof ~ "`.");
99 }
100 
101 /**
102  * `apply` can call regular functions with arguments known either at
103  * compile-time, or run-time:
104  */
105 @safe pure nothrow @nogc
106 unittest
107 {
108     int add(int a, int b) { return a + b; }
109     enum int a = 2, b = 3;
110     enum int sum = apply!(add, a, b);
111     static assert(sum == 5);
112 
113     int x = 2, y = 5;
114     int z = apply!((a, b) => a + b, x, y);
115     assert(z == 7);
116 
117     // TODO: Show higher-order template example
118 
119     // When `apply` is used to call functions it returns the result
120     // by reference (when possible):
121     static struct S { int x; }
122     static ref int getX(ref S s) { return s.x; }
123     static int inc(ref int x) { return ++x; }
124     auto s = S(41);
125     int theAnswer = apply!(inc, apply!(getX, s));
126     assert (theAnswer == 42);
127     assert (s.x == 42);
128 }
129 
130 /**
131  * `apply` can also instantiate function templates with compile-time parameters
132  * and alias or call them:
133  */
134 @safe nothrow @nogc
135 unittest
136 {
137     int mul(int a, int b)() { return a * b; }
138 
139     enum product = apply!(mul, 2, 7);
140     static assert (product == 14);
141 
142     static int counter = 0;
143     int offset(int x) { return 10 * ++counter + x; }
144     alias offset7 = apply!(offset, 7);
145 
146     assert(offset7()         == 17);
147     assert(apply!(offset, 7) == 27);
148     assert(offset7()         == 37);
149 }
150 
151 /// `apply` can instnatiate manifest constant (a.k.a. `enum`) templates:
152 @safe pure nothrow @nogc
153 unittest
154 {
155     enum ulong square(uint x) = x * x;
156     enum result = apply!(square, 3);
157     static assert(result == 9);
158 }
159 
160 /**
161  * Two or more `apply` instances can be chained to provide the template and
162  * later the run-time arguments of a function template:
163  */
164 @safe pure nothrow @nogc
165 unittest
166 {
167     static T[] transform(alias fun, T)(T[] arr)
168     {
169         foreach (ref e; arr)
170             e = fun(e);
171         return arr;
172     }
173 
174     alias result = apply!(
175         apply!(transform, x => x * x, int),
176         [2, 3, 4, 5]);
177 
178     static assert (result == [4, 9, 16, 25]);
179 }
180 
181 /// `apply` can instnatiate alias templates
182 @safe pure nothrow @nogc
183 unittest
184 {
185     alias ArrayOf(T) = T[];
186     alias ArrayType = apply!(ArrayOf, int);
187     static assert (is(ArrayType == int[]));
188 
189     import std.traits : CommonType;
190     alias Types = AliasSeq!(byte, short, int, long);
191     static assert (is(apply!(CommonType, Types) == long));
192 }
193 
194 /// `apply` can instnatiate higher-order templates that yield alias sequences:
195 @safe pure nothrow @nogc
196 unittest
197 {
198     import std.meta : staticMap;
199 
200     alias ArrayOf(T) = T[];
201     alias Types = AliasSeq!(byte, short, int, long);
202     alias ArrayTypes = apply!(staticMap, ArrayOf, Types);
203     static assert (is(ArrayTypes == AliasSeq!(byte[], short[], int[], long[])));
204 
205     template Overloads(T, string member)
206     {
207         alias Overloads = AliasSeq!(__traits(getOverloads, T, member));
208     }
209 
210     struct S
211     {
212         static int use(int x) { return x + 1; }
213         static bool use(char c) { return c >= '0' && c <= '9'; }
214         static char use(string s) { return s[0]; }
215     }
216 
217     static assert(apply!(Overloads, S, "use").length == 3);
218 
219     alias atIndex(size_t idx, List...) = Alias!(List[idx]);
220 
221     static assert(
222         apply!(  // 3) call the function with the argument `10`
223             apply!(  // 2) get the first element (with type `int use(int x)`)
224                 atIndex,
225                 0,
226                 apply!(Overloads, S, "use") // 1) get the `use` overload set
227             ),
228             10
229         ) == 11);
230 
231     static assert(
232         apply!(apply!(atIndex, 1, apply!(Overloads, S, "use")), '3') == true);
233 
234     alias useOverloadSet = Alias!(__traits(getOverloads, S, "use"))[0];
235     alias useOverloadSet = Alias!(__traits(getOverloads, S, "use"))[1];
236     alias useOverloadSet = Alias!(__traits(getOverloads, S, "use"))[2];
237 
238     static assert(useOverloadSet(41) == 42);
239     //static assert(useOverloadSet('@') == false);
240     //static assert(useOverloadSet('7') == true);
241     static assert(useOverloadSet("Voldemort") == 'V');
242 
243     template AliasTuple(T...)
244     {
245         alias expand = T;
246     }
247 
248     template applyN(alias fun, Tuples...)
249         if (Tuples.length > 0)
250     {
251         static if (Tuples.length == 1)
252             alias applyN = apply!(fun, Tuples[0].expand);
253         else
254             alias applyN = applyN!(
255                 apply!(fun, Tuples[0].expand),
256                 Tuples[1 .. $]);
257     }
258 
259     //alias AT = AliasTuple;
260     //applyN!(Overloads, AT!(S, "use"), atIndex, 2, "asd");
261     //applyN!("use", ApplyLeft!(Overloads, S), ApplyLeft!(atIndex, 2)
262 
263     static assert(
264         apply!(apply!(atIndex, 2, apply!(Overloads, S, "use")), "asd") == 'a');
265 }
266 
267 version (unittest)
268 {
269     template staticMap1(alias func, Args...) if (Args.length > 0)
270     {
271         static if (Args.length == 1)
272             alias staticMap1 = func!(Args[0]);
273         else
274             alias staticMap1 = AliasSeq!(
275                 func!(Args[0]),
276                 staticMap1!(func, Args[1 .. $]));
277     }
278 
279     template staticMap2(alias func, Args...) if (Args.length > 0)
280     {
281         static if (Args.length == 1)
282             alias staticMap2 = apply!(func, Args[0]);
283         else
284             alias staticMap2 = AliasSeq!(
285                 apply!(func, Args[0]),
286                 staticMap2!(func, Args[1 .. $]));
287     }
288 }
289 
290 // Undcoumented, because `staticMap1` and `staticMap2` can't be defined inside
291 // the unittest.
292 @safe pure nothrow @nogc
293 unittest
294 {
295     enum square(int x) = x * x;
296     alias numbers = AliasSeq!(1, 2, 3);
297 
298     static assert (!__traits(compiles,
299         { alias result = staticMap1!(x => x * x, numbers); }));
300 
301     alias result = staticMap2!(x => x * x, numbers);
302     static assert([result] == [1, 4, 9]);
303 }
304 
305 //template staticMapReduce(al
306 
307 
308 template allMatchFirst(alias pred, list...)
309     if (list.length >= 2)
310 {
311     //static if (is(typeof(pred(list[0], list[1])) : bool))
312     //{
313     //    static if (list.length == 2)
314     //        bool allMatchFirst = pred(list[0], list[1]);
315 
316       //  else {}
317             //bool allMatchFirst =
318              //   allPairs(
319     //}
320     //else (is(typeof(pred!(list[0], list[1])): bool))
321     //{
322    // }
323     //else
324      //   static assert (0, "Can't call/instantiate pred with ", List.stringof);
325 }
326 
327 
328 template allPairs(alias pred, List...)
329 {
330     alias first = List[0];
331     alias rest = List[1 .. $];
332 
333     //static if (is(typeof(pred(List[0], List[1]))))
334 
335 
336 
337 }
338 
339 alias TypeOf(T) = T;
340 alias TypeOf(alias sym) = typeof(sym);
341 
342 template sameFunctionSignature(alias F1, alias F2)
343 {
344     import std.traits : isSomeFunction;
345 
346     enum bool sameFunctionSignature =
347         isSomeFunction!F1 && isSomeFunction!F2 &&
348         is(TypeOf!F1 == TypeOf!F2);
349 }
350 
351 unittest
352 {
353     import std.meta : AliasSeq;
354     void f0(int x, int y);
355     void f1(int x, int y);
356     void f2(short x, short y);
357     void f3(const int x, const int y);
358     void f4(ref int x, ref int y);
359     void f5(scope int x, scope int y);
360 
361     alias all = AliasSeq!(f1, f2, f3, f4, f5);
362 
363     //static foreach (idx, f1; all[0 .. $ - 1)
364     //static foreach (f2; all[idx .. $])
365     //static assert (sameFunctionSignature!(f0, f1));
366 
367 }
368 
369 alias Func(RT, Args...) = RT function(Args);
370 
371 /// Checks if `Model`'s parameter types are convertible to `Func`'s
372 /// parameter types and `Func`'s return type can be converted
373 /// to Model's return type.
374 template isFuncLike(Func, Model)
375 {
376     import std.traits : ReturnType, Parameters, isImplicitlyConvertible;
377     import rxd.meta2.type : AliasTuple, TypeSeq;
378 
379     enum isFuncLike =
380         is(ReturnType!Func : ReturnType!Model) &&
381         allConvertible!(
382             AliasTuple!(TypeSeq!(Parameters!Model)),
383             AliasTuple!(TypeSeq!(Parameters!Func)));
384 }
385 
386 ///
387 unittest
388 {
389     struct S
390     {
391         int   f1(int)   { return 0; }
392         float f2(int)   { return 0; }
393         int   f3(float) { return 0; }
394         float f4(float) { return 0; }
395     }
396 
397     static assert ( isFuncLike!(typeof(&S.f1), Func!(int, int)));
398     static assert (!isFuncLike!(typeof(&S.f2), Func!(int, int)));
399     static assert ( isFuncLike!(typeof(&S.f3), Func!(int, int)));
400     static assert (!isFuncLike!(typeof(&S.f4), Func!(int, int)));
401 
402     static assert ( isFuncLike!(typeof(&S.f1), Func!(float, int)));
403     static assert ( isFuncLike!(typeof(&S.f2), Func!(float, int)));
404     static assert ( isFuncLike!(typeof(&S.f3), Func!(float, int)));
405     static assert ( isFuncLike!(typeof(&S.f4), Func!(float, int)));
406 
407     static assert (!isFuncLike!(typeof(&S.f1), Func!(int, float)));
408     static assert (!isFuncLike!(typeof(&S.f2), Func!(int, float)));
409     static assert ( isFuncLike!(typeof(&S.f3), Func!(int, float)));
410     static assert (!isFuncLike!(typeof(&S.f4), Func!(int, float)));
411 
412     static assert (!isFuncLike!(typeof(&S.f1), Func!(float, float)));
413     static assert (!isFuncLike!(typeof(&S.f2), Func!(float, float)));
414     static assert ( isFuncLike!(typeof(&S.f3), Func!(float, float)));
415     static assert ( isFuncLike!(typeof(&S.f4), Func!(float, float)));
416 }
417 
418 unittest
419 {
420     class Person {}
421     class Employee : Person {}
422 
423     Person   g1(Person)   { return null; }
424     Person   g2(Employee) { return null; }
425     Person   g3(Object)   { return null; }
426     Employee g4(Person)   { return null; }
427     Employee g5(Employee) { return null; }
428     Employee g6(Object)   { return null; }
429     Object   g7(Person)   { return null; }
430     Object   g8(Employee) { return null; }
431     Object   g9(Object)   { return null; }
432 
433 
434 }