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