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