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