1 /**
2  * Metaprogramming
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.meta;
15 
16 public import ae.utils.meta.reference;
17 public import ae.utils.meta.x;
18 public import ae.utils.meta.proxy;
19 public import ae.utils.meta.binding_v1;
20 public import ae.utils.meta.binding;
21 
22 // ************************************************************************
23 
24 import std.algorithm;
25 import std.range;
26 import std.traits;
27 import std.typetuple;
28 
29 /**
30  * Same as TypeTuple, but meant to be used with values.
31  *
32  * Example:
33  *   foreach (char channel; ValueTuple!('r', 'g', 'b'))
34  *   {
35  *     // the loop is unrolled at compile-time
36  *     // "channel" is a compile-time value, and can be used in string mixins
37  *   }
38  */
39 template ValueTuple(T...)
40 {
41 	alias T ValueTuple;
42 }
43 
44 template RangeTupleImpl(size_t N, R...)
45 {
46 	static if (N==R.length)
47 		alias R RangeTupleImpl;
48 	else
49 		alias RangeTupleImpl!(N, ValueTuple!(R, R.length)) RangeTupleImpl;
50 }
51 
52 /// Generate a tuple containing integers from 0 to N-1.
53 /// Useful for static loop unrolling. (staticIota)
54 template RangeTuple(size_t N)
55 {
56 	alias RangeTupleImpl!(N, ValueTuple!()) RangeTuple;
57 }
58 
59 template ArrayToTuple(alias arr, Elements...)
60 {
61 	static if (arr.length)
62 		alias ArrayToTuple = ArrayToTuple!(arr[1..$], ValueTuple!(Elements, arr[0]));
63 	else
64 		alias ArrayToTuple = Elements;
65 }
66 
67 unittest
68 {
69 	alias X = ArrayToTuple!"abc";
70 	static assert(X[0] == 'a' && X[2] == 'c');
71 	static assert([X] == "abc");
72 }
73 
74 /// Return something to foreach over optimally.
75 /// If A is known at compile-time, return a tuple,
76 /// so the foreach is unrolled at compile-time.
77 /// Otherwise, return A for a regular runtime foreach.
78 template CTIterate(alias A)
79 {
80 	static if (is(typeof(ArrayToTuple!A)))
81 		enum CTIterate = ArrayToTuple!A;
82 	else
83 		alias CTIterate = A;
84 }
85 
86 unittest
87 {
88 	foreach (c; CTIterate!"abc") {}
89 	string s;
90 	foreach (c; CTIterate!s) {}
91 }
92 
93 /// Like std.typecons.Tuple, but a template mixin.
94 /// Unlike std.typecons.Tuple, names may not be omitted - but repeating types may be.
95 /// Example: FieldList!(ubyte, "r", "g", "b", ushort, "a");
96 mixin template FieldList(Fields...)
97 {
98 	mixin(GenFieldList!(void, Fields));
99 }
100 
101 template GenFieldList(T, Fields...)
102 {
103 	static if (Fields.length == 0)
104 		enum GenFieldList = "";
105 	else
106 	{
107 		static if (is(typeof(Fields[0]) == string))
108 			enum GenFieldList = T.stringof ~ " " ~ Fields[0] ~ ";\n" ~ GenFieldList!(T, Fields[1..$]);
109 		else
110 			enum GenFieldList = GenFieldList!(Fields[0], Fields[1..$]);
111 	}
112 }
113 
114 unittest
115 {
116 	struct S
117 	{
118 		mixin FieldList!(ubyte, "r", "g", "b", ushort, "a");
119 	}
120 	S s;
121 	static assert(is(typeof(s.r) == ubyte));
122 	static assert(is(typeof(s.g) == ubyte));
123 	static assert(is(typeof(s.b) == ubyte));
124 	static assert(is(typeof(s.a) == ushort));
125 }
126 
127 /// Return true if all of T's fields are the same type.
128 @property bool isHomogenous(T)()
129 {
130 	foreach (i, f; T.init.tupleof)
131 		if (!is(typeof(T.init.tupleof[i]) == typeof(T.init.tupleof[0])))
132 			return false;
133 	return true;
134 }
135 
136 template isValueOfTypeInTuple(X, T...)
137 {
138 	static if (T.length==0)
139 		enum bool isValueOfTypeInTuple = false;
140 	else
141 	static if (T.length==1)
142 		enum bool isValueOfTypeInTuple = is(typeof(T[0]) : X);
143 	else
144 		enum bool isValueOfTypeInTuple = isValueOfTypeInTuple!(X, T[0..$/2]) || isValueOfTypeInTuple!(X, T[$/2..$]);
145 }
146 
147 unittest
148 {
149 	static assert( isValueOfTypeInTuple!(int, ValueTuple!("a", 42)));
150 	static assert(!isValueOfTypeInTuple!(int, ValueTuple!("a", 42.42)));
151 	static assert(!isValueOfTypeInTuple!(int, ValueTuple!()));
152 
153 	static assert(!isValueOfTypeInTuple!(int, "a", int, Object));
154 	static assert( isValueOfTypeInTuple!(int, "a", int, Object, 42));
155 }
156 
157 template findValueOfTypeInTuple(X, T...)
158 {
159 	static if (T.length==0)
160 		static assert(false, "Can't find value of type " ~ X.stringof ~ " in specified tuple");
161 	else
162 	static if (is(typeof(T[0]) : X))
163 		enum findValueOfTypeInTuple = T[0];
164 	else
165 		enum findValueOfTypeInTuple = findValueOfTypeInTuple!(X, T[1..$]);
166 }
167 
168 unittest
169 {
170 	static assert(findValueOfTypeInTuple!(int, ValueTuple!("a", 42))==42);
171 	static assert(findValueOfTypeInTuple!(int, "a", int, Object, 42)==42);
172 }
173 
174 /// One past the biggest element of the enum T.
175 /// Example: string[enumLength!E] arr;
176 template enumLength(T)
177 	if (is(T==enum))
178 {
179 	enum enumLength = cast(T)(cast(size_t)T.max + 1);
180 }
181 
182 deprecated alias EnumLength = enumLength;
183 
184 /// A range that iterates over all members of an enum.
185 @property auto enumIota(T)() { return iota(T.init, enumLength!T); }
186 
187 unittest
188 {
189 	enum E { a, b, c }
190 	static assert(equal(enumIota!E, [E.a, E.b, E.c]));
191 }
192 
193 // ************************************************************************
194 
195 // http://d.puremagic.com/issues/show_bug.cgi?id=7805
196 static template stringofArray(Args...)
197 {
198 	static string[] stringofArray()
199 	{
200 		string[] args;
201 		foreach (i, _ ; typeof(Args))
202 			args ~= Args[i].stringof;
203 		return args;
204 	}
205 }
206 
207 /// Returns the index of fun's parameter with the name
208 /// matching "names", or asserts if the parameter is not found.
209 /// "names" can contain multiple names separated by slashes.
210 static size_t findParameter(alias fun, string names)()
211 {
212 	foreach (name; names.split("/"))
213 		foreach (i, param; ParameterIdentifierTuple!fun)
214 			if (param == name)
215 				return i;
216 	assert(false, "Function " ~ __traits(identifier, fun) ~ " doesn't have a parameter called " ~ name);
217 }
218 
219 /// ditto
220 // Workaround for no "static alias" template parameters
221 static size_t findParameter()(string[] searchedNames, string soughtNames, string funName)
222 {
223 	foreach (soughtName; soughtNames.split("/"))
224 	{
225 		auto targetIndex = searchedNames.countUntil(soughtName);
226 		if (targetIndex >= 0)
227 			return targetIndex;
228 	}
229 	assert(false, "No argument %s in %s's parameters (%s)".format(soughtNames, funName, searchedNames).idup);
230 }
231 
232 /// Generates a function which passes its arguments to a struct, which is
233 /// returned. Preserves field names (as parameter names) and default values.
234 template structFun(S)
235 {
236 	string gen()
237 	{
238 		enum identifierAt(int n) = __traits(identifier, S.tupleof[n]);
239 		enum names = [staticMap!(identifierAt, RangeTuple!(S.tupleof.length))];
240 
241 		return
242 			"S structFun(\n" ~
243 			S.tupleof.length.iota.map!(n =>
244 			"	typeof(S.init.tupleof[%d]) %s = S.init.tupleof[%d],\n".format(n, names[n], n)
245 			).join() ~
246 			`) { return S(` ~ names.join(", ") ~ "); }";
247 	}
248 
249 	mixin(gen());
250 }
251 
252 unittest
253 {
254 	static struct Test
255 	{
256 		string a;
257 		int b = 42;
258 	}
259 
260 	Test test = structFun!Test("banana");
261 	assert(test.a is "banana");
262 	assert(test.b == 42);
263 }
264 
265 // ************************************************************************
266 
267 // Using a compiler with UDA support?
268 enum HAVE_UDA = __traits(compiles, __traits(getAttributes, Object));
269 
270 static if (HAVE_UDA)
271 {
272 	template hasAttribute(T, alias D)
273 	{
274 		enum bool hasAttribute = isValueOfTypeInTuple!(T, __traits(getAttributes, D));
275 	}
276 
277 	template getAttribute(T, alias D)
278 	{
279 		enum T getAttribute = findValueOfTypeInTuple!(T, __traits(getAttributes, D));
280 	}
281 }
282 else
283 {
284 	template hasAttribute(T, alias D)
285 	{
286 		enum bool hasAttribute = false;
287 	}
288 
289 	template getAttribute(T, alias D)
290 	{
291 		static assert(false, "This D compiler has no UDA support.");
292 	}
293 }
294 
295 // ************************************************************************
296 
297 import std.conv;
298 import std..string;
299 
300 string mixGenerateContructorProxies(T)()
301 {
302 	string s;
303 	static if (__traits(hasMember, T, "__ctor"))
304 		foreach (ctor; __traits(getOverloads, T, "__ctor"))
305 		{
306 			string[] declarationList, usageList;
307 			foreach (i, param; ParameterTypeTuple!(typeof(&ctor)))
308 			{
309 				auto varName = "v" ~ text(i);
310 				declarationList ~= param.stringof ~ " " ~ varName;
311 				usageList ~= varName;
312 			}
313 			s ~= "this(" ~ declarationList.join(", ") ~ ") { super(" ~ usageList.join(", ") ~ "); }\n";
314 		}
315 	return s;
316 }
317 
318 /// Generate constructors that simply call the parent class constructors.
319 /// Based on http://forum.dlang.org/post/i3hpj0$2vc6$1@digitalmars.com
320 mixin template GenerateContructorProxies()
321 {
322 	mixin(mixGenerateContructorProxies!(typeof(super))());
323 }
324 
325 unittest
326 {
327 	class A
328 	{
329 		int i, j;
330 		this() { }
331 		this(int i) { this.i = i; }
332 		this(int i, int j ) { this.i = i; this.j = j; }
333 	}
334 
335 	class B : A
336 	{
337 		mixin GenerateContructorProxies;
338 	}
339 
340 	A a;
341 
342 	a = new B();
343 	assert(a.i == 0);
344 	a = new B(17);
345 	assert(a.i == 17);
346 	a = new B(17, 42);
347 	assert(a.j == 42);
348 }
349 
350 // ************************************************************************
351 
352 /// Generate a @property function which creates/returns
353 /// a thread-local singleton of a class with the given arguments.
354 
355 @property T singleton(T, args...)()
356 	if (is(typeof(new T(args))))
357 {
358 	static T instance;
359 	if (!instance)
360 		instance = new T(args);
361 	return instance;
362 }
363 
364 unittest
365 {
366 	static class C
367 	{
368 		static int n = 0;
369 
370 		this()      { n++; }
371 		this(int x) { n += x; }
372 
373 		void fun() {}
374 	}
375 
376 	alias singleton!C c0;
377 	c0.fun();
378 	c0.fun();
379 	assert(C.n == 1);
380 
381 	alias singleton!(C, 5) c1;
382 	c1.fun();
383 	c1.fun();
384 	assert(C.n == 6);
385 }
386 
387 // ************************************************************************
388 
389 /// Were we built with -debug?
390 debug
391 	enum isDebug = true;
392 else
393 	enum isDebug = false;
394 
395 deprecated alias IsDebug = isDebug;
396 
397 /// Is a specific version on?
398 template isVersion(string versionName)
399 {
400 	mixin(`version (` ~ versionName ~ `) enum isVersion = true; else enum isVersion = false;`);
401 }
402 
403 // ************************************************************************
404 
405 /// Shorter synonym for std.traits.Identity.
406 /// Can be used to UFCS-chain static methods and nested functions.
407 alias I(alias A) = A;
408 
409 /// Get f's ancestor which represents its "this" pointer.
410 /// Skips template and mixin ancestors until it finds a struct or class.
411 template thisOf(alias f)
412 {
413 	alias p = Identity!(__traits(parent, f));
414 	static if (is(p == class) || is(p == struct) || is(p == union))
415 		alias thisOf = p;
416 	else
417 		alias thisOf = thisOf!p;
418 }
419 
420 // ************************************************************************
421 
422 /// Return the number of bits used to store the value part, i.e.
423 /// T.sizeof*8 for integer parts and the mantissa size for
424 /// floating-point types.
425 template valueBits(T)
426 {
427 	static if (is(T : ulong))
428 		enum valueBits = T.sizeof * 8;
429 	else
430 	static if (is(T : real))
431 		enum valueBits = T.mant_dig;
432 	else
433 		static assert(false, "Don't know how many value bits there are in " ~ T.stringof);
434 }
435 
436 static assert(valueBits!uint == 32);
437 static assert(valueBits!double == 53);
438 
439 /// Expand to a built-in numeric type of the same kind
440 /// (signed integer / unsigned integer / floating-point)
441 /// with at least the indicated number of bits of precision.
442 template ResizeNumericType(T, uint bits)
443 {
444 	static if (is(T : ulong))
445 		static if (isSigned!T)
446 			alias ResizeNumericType = SignedBitsType!bits;
447 		else
448 			alias ResizeNumericType = UnsignedBitsType!bits;
449 	else
450 	static if (is(T : real))
451 	{
452 		static if (bits <= float.mant_dig)
453 			alias ResizeNumericType = float;
454 		else
455 		static if (bits <= double.mant_dig)
456 			alias ResizeNumericType = double;
457 		else
458 		static if (bits <= real.mant_dig)
459 			alias ResizeNumericType = real;
460 		else
461 			static assert(0, "No floating-point type big enough to fit " ~ bits.stringof ~ " bits");
462 	}
463 	else
464 		static assert(false, "Don't know how to resize type: " ~ T.stringof);
465 }
466 
467 static assert(is(ResizeNumericType!(float, double.mant_dig) == double));
468 
469 /// Expand to a built-in numeric type of the same kind
470 /// (signed integer / unsigned integer / floating-point)
471 /// with at least additionalBits more bits of precision.
472 alias ExpandNumericType(T, uint additionalBits) =
473 	ResizeNumericType!(T, valueBits!T + additionalBits);
474 
475 /// Unsigned integer type big enough to fit N bits of precision.
476 template UnsignedBitsType(uint bits)
477 {
478 	static if (bits <= 8)
479 		alias ubyte UnsignedBitsType;
480 	else
481 	static if (bits <= 16)
482 		alias ushort UnsignedBitsType;
483 	else
484 	static if (bits <= 32)
485 		alias uint UnsignedBitsType;
486 	else
487 	static if (bits <= 64)
488 		alias ulong UnsignedBitsType;
489 	else
490 		static assert(0, "No integer type big enough to fit " ~ bits.stringof ~ " bits");
491 }
492 
493 template SignedBitsType(uint bits)
494 {
495 	alias Signed!(UnsignedBitsType!bits) SignedBitsType;
496 }
497 
498 /// Evaluates to array of strings with name for each field.
499 @property string[] structFields(T)()
500 	if (is(T == struct) || is(T == class))
501 {
502 	import std..string : split;
503 
504 	string[] fields;
505 	foreach (i, f; T.init.tupleof)
506 	{
507 		string field = T.tupleof[i].stringof;
508 		field = field.split(".")[$-1];
509 		fields ~= field;
510 	}
511 	return fields;
512 }