1 module nxt.meta_ex;
2 
3 @safe:
4 
5 import std.meta : AliasSeq, aliasSeqOf;
6 
7 @safe pure nothrow @nogc unittest
8 {
9     import std.range : iota;
10     foreach (_; aliasSeqOf!(iota(10)))
11     {
12         // pragma(msg, i);
13     }
14 }
15 
16 /**
17    See_Also: http://forum.dlang.org/post/sulxqtfprmkeekjatqup@forum.dlang.org
18 */
19 template Merge1(A...)
20 if (!(A.length & 1))
21 {
22     static if (A.length == 0)
23     {
24         alias Merge1 = AliasSeq!();
25     }
26     else
27     {
28         alias Left = A[0 .. $ / 2];
29         alias Right = A[$ / 2 .. $];
30         alias Merge1 = AliasSeq!(Left[0], Right[0], Merge1!(Left[1 .. $], Right[1 .. $]));
31     }
32 }
33 
34 @safe pure nothrow @nogc unittest
35 {
36     struct S(A...) {} // needed to reliably compare AliasSeq's for equality
37 
38     alias first = AliasSeq!(int, string, bool);
39     alias second = AliasSeq!("abc", "def", "ghi");
40     alias third = Merge1!(first, second);
41 
42     static assert(is(S!third == S!(int,    "abc",
43                                    string, "def",
44                                    bool,   "ghi")));
45 }
46 
47 /**
48    See_Also: http://forum.dlang.org/post/sulxqtfprmkeekjatqup@forum.dlang.org
49 */
50 template Merge(A...)
51 {
52     template With(B...)
53     {
54         static if (A.length == 0 ||
55                    B.length == 0)
56             alias With = AliasSeq!(A, B); // or static assert(0) if you require equal lengths
57         else
58             alias With = AliasSeq!(A[0], B[0], Merge!(A[1 .. $]).With!(B[1 .. $]));
59     }
60 }
61 
62 @safe pure nothrow @nogc unittest
63 {
64     struct S(A...) {} // needed to reliably compare AliasSeq's for equality
65 
66     alias first = AliasSeq!(int, string, bool);
67     alias second = AliasSeq!("abc", "def", "ghi");
68     alias third = Merge!first.With!second;
69 
70     static assert(is(S!third == S!(int, "abc",
71                                    string, "def",
72                                    bool, "ghi")));
73 
74     alias fourth = Merge!(first[0 .. 2]).With!second;
75 
76     static assert(is(S!fourth == S!(int, "abc",
77                                     string, "def",
78                                     "ghi")));
79 }
80 
81 /** Mixin for generating `struct` member `byRef`.
82     See_Also: http://forum.dlang.org/post/o0vk14$2j89$1@digitalmars.com
83  */
84 mixin template RvalueRef()
85 {
86     alias T = typeof(this);
87     static assert (is(T == struct));
88 
89     @nogc @safe
90     ref const(T) byRef() const return
91     {
92         return this;
93     }
94 }
95 
96 @safe pure nothrow @nogc unittest
97 {
98     struct Vector
99     {
100         float x, y;
101         mixin RvalueRef;
102     }
103 
104     void useVector(ref const Vector pos) {}
105 
106     Vector v = Vector(42, 23);
107 
108     useVector(v);                     // works
109     useVector(Vector(42, 23).byRef);  // works as well, and use the same function
110 }
111 
112 // Use same as staticIndexOf
113 template staticAssignableTypeIndexOf(U)
114 {
115     static auto f(U)()
116     {
117         static foreach (i, T; Types)
118         {
119             import std.traits : isAssignable;
120             static if (isAssignable!(T, U))
121             {
122                 return i;
123             }
124         }
125         return 0;
126     }
127     enum canStore = f!U;
128 }
129 
130 import std.functional : unaryFun;
131 
132 /** Returns: `xs` forwarded through calls to `fun` and packed into a `std.typecons.Tuple`.
133  *
134  * See_Also: https://forum.dlang.org/post/zjxmreegqkxgdzvihvyk@forum.dlang.org
135  */
136 auto forwardMap(alias fun, Ts...)(Ts xs)
137 if (is(typeof(unaryFun!(fun))))
138 {
139     import std.meta : staticMap;
140     alias MappedTypeOf(T) = typeof(fun(T.init));
141     alias NewTypes = staticMap!(MappedTypeOf, Ts);
142 
143     import std.typecons : Tuple;
144     Tuple!NewTypes ys = void;
145 
146     alias fun_ = unaryFun!(fun);
147 
148     import core.lifetime : emplace;
149     static foreach (immutable i, x; xs)
150     {
151         emplace(&ys[i], fun_(x));
152     }
153 
154     return ys;
155 }
156 
157 @safe pure unittest
158 {
159     import std.typecons : Tuple;
160     alias X = Tuple!(int, float, double);
161     auto x = X(42, 42f, 42);
162     auto y = forwardMap!(_ => _ + 1)(x.tupleof);
163     static assert(is(typeof(x) == typeof(y)));
164     assert(y == X(43, 43f, 43));
165 }
166 
167 /** Flattens a list `Values` of ranges and non ranges.
168  *
169  * If a type is a range then its `ElementType` is used.
170  */
171 template FlattenedRanges(Values...)
172 {
173     import std.meta : AliasSeq;
174     static if (Values.length)
175     {
176         import std.range.primitives : isInputRange;
177         alias Head = Values[0];
178         alias Tail = Values[1 .. $];
179         static if (isInputRange!Head)
180         {
181             import std.range.primitives : ElementType;
182             alias FlattenedRanges = FlattenedRanges!(ElementType!Head, FlattenedRanges!Tail);
183         }
184         else
185         {
186             alias FlattenedRanges = AliasSeq!(Head, FlattenedRanges!Tail);
187         }
188     }
189     else
190     {
191         alias FlattenedRanges = AliasSeq!();
192     }
193 }
194 
195 ///
196 @safe unittest
197 {
198     import std.algorithm : filter;
199     import std.meta : AliasSeq;
200 
201     alias R1 = typeof([1, 2, 3].filter!"true");
202     alias R2 = typeof([1.0, 2.0, 3.0]);
203 
204     static assert(is(FlattenedRanges!(int, double) == AliasSeq!(int, double)));
205     static assert(is(FlattenedRanges!(int, R1, R2) == AliasSeq!(int, int, double)));
206 
207     import std.traits : CommonType;
208 
209     static assert(is(CommonType!(FlattenedRanges!(int, R1, R2, float)) == double));
210 }
211 
212 /** Returns the types of all values given.
213  *
214  * If a `T` is an expression it is resolved with `typeof` else it is just
215  * appended.
216  *
217  * Returns: `AliasSeq` of the resulting types
218 */
219 template TypesOf(Values...)
220 {
221     import std.meta : AliasSeq;
222     import std.traits : isExpressions;
223     static if (Values.length)
224     {
225         static if (isExpressions!(Values[0]))
226         {
227             alias T = typeof(Values[0]);
228         }
229         else
230         {
231             alias T = Values[0];
232         }
233         alias TypesOf = AliasSeq!(T, TypesOf!(Values[1 .. $]));
234     }
235     else
236     {
237         alias TypesOf = AliasSeq!();
238     }
239 }
240 
241 ///
242 @safe pure nothrow @nogc unittest
243 {
244     import std.meta : AliasSeq;
245     static assert(is(TypesOf!("hello", 1, 2, 3.0, real) ==
246                      AliasSeq!(string, int, int, double, real)));
247 }
248 
249 /** Can be used to construct a meta function that checks if a symbol is of a type.
250  */
251 template typeOf(T)
252 {
253     auto typeOf(U)(U)
254     {
255         return is(U == T);
256     }
257     enum typeOf(alias a) = typeOf!T(a);
258 }
259 
260 ///
261 @safe pure nothrow @nogc unittest
262 {
263     import std.meta : allSatisfy;
264     static assert(typeOf!int(3));
265     static assert(allSatisfy!(typeOf!int, 3));
266 }
267 
268 template from(string moduleName)
269 {
270     mixin("import nxt.from = " ~ moduleName ~ ";");
271 }