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 => this;
91 }
92 
93 @safe pure nothrow @nogc unittest
94 {
95     struct Vector
96     {
97         float x, y;
98         mixin RvalueRef;
99     }
100 
101     void useVector(ref const Vector pos) {}
102 
103     Vector v = Vector(42, 23);
104 
105     useVector(v);                     // works
106     useVector(Vector(42, 23).byRef);  // works as well, and use the same function
107 }
108 
109 // Use same as staticIndexOf
110 template staticAssignableTypeIndexOf(U)
111 {
112     static auto f(U)()
113     {
114 		import std.traits : isAssignable;
115         static foreach (i, T; Types)
116             static if (isAssignable!(T, U))
117                 return i;
118         return 0;
119     }
120     enum canStore = f!U;
121 }
122 
123 import std.functional : unaryFun;
124 
125 /** Returns: `xs` forwarded through calls to `fun` and packed into a `std.typecons.Tuple`.
126  *
127  * See_Also: https://forum.dlang.org/post/zjxmreegqkxgdzvihvyk@forum.dlang.org
128  */
129 auto forwardMap(alias fun, Ts...)(Ts xs)
130 if (is(typeof(unaryFun!(fun))))
131 {
132     import std.meta : staticMap;
133     alias MappedTypeOf(T) = typeof(fun(T.init));
134     alias NewTypes = staticMap!(MappedTypeOf, Ts);
135 
136     import std.typecons : Tuple;
137     Tuple!NewTypes ys = void;
138 
139     alias fun_ = unaryFun!(fun);
140 
141     import core.lifetime : emplace;
142     static foreach (immutable i, x; xs)
143         emplace(&ys[i], fun_(x));
144 
145     return ys;
146 }
147 
148 @safe pure unittest
149 {
150     import std.typecons : Tuple;
151     alias X = Tuple!(int, float, double);
152     auto x = X(42, 42f, 42);
153     auto y = forwardMap!(_ => _ + 1)(x.tupleof);
154     static assert(is(typeof(x) == typeof(y)));
155     assert(y == X(43, 43f, 43));
156 }
157 
158 /** Flattens a list `Values` of ranges and non ranges.
159  *
160  * If a type is a range then its `ElementType` is used.
161  */
162 template FlattenedRanges(Values...)
163 {
164     import std.meta : AliasSeq;
165     static if (Values.length)
166     {
167         import std.range.primitives : isInputRange;
168         alias Head = Values[0];
169         alias Tail = Values[1 .. $];
170         static if (isInputRange!Head)
171         {
172             import std.range.primitives : ElementType;
173             alias FlattenedRanges = FlattenedRanges!(ElementType!Head, FlattenedRanges!Tail);
174         }
175         else
176             alias FlattenedRanges = AliasSeq!(Head, FlattenedRanges!Tail);
177     }
178     else
179         alias FlattenedRanges = AliasSeq!();
180 }
181 
182 ///
183 @safe unittest
184 {
185     import std.algorithm : filter;
186     import std.meta : AliasSeq;
187 
188     alias R1 = typeof([1, 2, 3].filter!"true");
189     alias R2 = typeof([1.0, 2.0, 3.0]);
190 
191     static assert(is(FlattenedRanges!(int, double) == AliasSeq!(int, double)));
192     static assert(is(FlattenedRanges!(int, R1, R2) == AliasSeq!(int, int, double)));
193 
194     import std.traits : CommonType;
195 
196     static assert(is(CommonType!(FlattenedRanges!(int, R1, R2, float)) == double));
197 }
198 
199 /** Returns the types of all values given.
200  *
201  * If a `T` is an expression it is resolved with `typeof` else it is just
202  * appended.
203  *
204  * Returns: `AliasSeq` of the resulting types
205 */
206 template TypesOf(Values...)
207 {
208     import std.meta : AliasSeq;
209     import std.traits : isExpressions;
210     static if (Values.length)
211     {
212         static if (isExpressions!(Values[0]))
213             alias T = typeof(Values[0]);
214         else
215             alias T = Values[0];
216         alias TypesOf = AliasSeq!(T, TypesOf!(Values[1 .. $]));
217     }
218     else
219         alias TypesOf = AliasSeq!();
220 }
221 
222 ///
223 @safe pure nothrow @nogc unittest
224 {
225     import std.meta : AliasSeq;
226     static assert(is(TypesOf!("hello", 1, 2, 3.0, real) ==
227                      AliasSeq!(string, int, int, double, real)));
228 }
229 
230 /** Can be used to construct a meta function that checks if a symbol is of a type.
231  */
232 template typeOf(T)
233 {
234     auto typeOf(U)(U) => is(U == T);
235     enum typeOf(alias a) = typeOf!T(a);
236 }
237 
238 ///
239 @safe pure nothrow @nogc unittest
240 {
241     import std.meta : allSatisfy;
242     static assert(typeOf!int(3));
243     static assert(allSatisfy!(typeOf!int, 3));
244 }
245 
246 template from(string moduleName)
247 {
248     mixin("import nxt.from = " ~ moduleName ~ ";");
249 }