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 import ae.utils.meta.caps;
23 
24 // ************************************************************************
25 
26 import std.traits;
27 
28 /**
29  * Same as TypeTuple, but meant to be used with values.
30  *
31  * Example:
32  *   foreach (char channel; ValueTuple!('r', 'g', 'b'))
33  *   {
34  *     // the loop is unrolled at compile-time
35  *     // "channel" is a compile-time value, and can be used in string mixins
36  *   }
37  */
38 template ValueTuple(T...)
39 {
40 	alias T ValueTuple;
41 }
42 
43 template RangeTupleImpl(size_t N, R...)
44 {
45 	static if (N==R.length)
46 		alias R RangeTupleImpl;
47 	else
48 		alias RangeTupleImpl!(N, ValueTuple!(R, R.length)) RangeTupleImpl;
49 }
50 
51 /// Generate a tuple containing integers from 0 to N-1.
52 /// Useful for static loop unrolling. (staticIota)
53 template RangeTuple(size_t N)
54 {
55 	alias RangeTupleImpl!(N, ValueTuple!()) RangeTuple;
56 }
57 
58 /// Expand an array to a tuple.
59 /// The array value must be known during compilation.
60 template ArrayToTuple(alias arr, Elements...)
61 {
62 	static if (arr.length)
63 		alias ArrayToTuple = ArrayToTuple!(arr[1..$], ValueTuple!(Elements, arr[0]));
64 	else
65 		alias ArrayToTuple = Elements;
66 }
67 
68 unittest
69 {
70 	alias X = ArrayToTuple!"abc";
71 	static assert(X[0] == 'a' && X[2] == 'c');
72 	static assert([X] == "abc");
73 }
74 
75 /// Expand a static array to a tuple.
76 /// Unlike ArrayToTuple, the array may be a runtime variable.
77 template expand(alias arr, size_t offset = 0)
78 	if (isStaticArray!(typeof(arr)))
79 {
80 	import std.typetuple : AliasSeq;
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)()
210 {
211 	import std.range : iota;
212 	return iota(T.init, enumLength!T);
213 }
214 
215 unittest
216 {
217 	import std.algorithm.comparison : equal;
218 	enum E { a, b, c }
219 	static assert(equal(enumIota!E, [E.a, E.b, E.c]));
220 }
221 
222 // ************************************************************************
223 
224 // http://d.puremagic.com/issues/show_bug.cgi?id=7805
225 static template stringofArray(Args...)
226 {
227 	static string[] stringofArray()
228 	{
229 		string[] args;
230 		foreach (i, _ ; typeof(Args))
231 			args ~= Args[i].stringof;
232 		return args;
233 	}
234 }
235 
236 /// Returns the index of fun's parameter with the name
237 /// matching "names", or asserts if the parameter is not found.
238 /// "names" can contain multiple names separated by slashes.
239 static size_t findParameter(alias fun, string names)()
240 {
241 	import std.array : split;
242 
243 	foreach (name; names.split("/"))
244 		foreach (i, param; ParameterIdentifierTuple!fun)
245 			if (param == name)
246 				return i;
247 	assert(false, "Function " ~ __traits(identifier, fun) ~ " doesn't have a parameter called " ~ names);
248 }
249 
250 /// ditto
251 // Workaround for no "static alias" template parameters
252 static size_t findParameter()(string[] searchedNames, string soughtNames, string funName)
253 {
254 	import std.array : split;
255 
256 	foreach (soughtName; soughtNames.split("/"))
257 	{
258 		import std.algorithm.searching : countUntil;
259 
260 		auto targetIndex = searchedNames.countUntil(soughtName);
261 		if (targetIndex >= 0)
262 			return targetIndex;
263 	}
264 
265 	{
266 		import std.format : format;
267 
268 		assert(false, "No argument %s in %s's parameters (%s)"
269 			.format(soughtNames, funName, searchedNames).idup);
270 	}
271 }
272 
273 unittest
274 {
275 	static void fun(int a, int b, int c) {}
276 
277 	static assert(findParameter!(fun, "x/c") == 2);
278 	assert(findParameter(["a", "b", "c"], "x/c", "fun") == 2);
279 }
280 
281 /// Generates a function which passes its arguments to a struct, which is
282 /// returned. Preserves field names (as parameter names) and default values.
283 template structFun(S)
284 {
285 	string gen()
286 	{
287 		import std.algorithm.iteration : map;
288 		import std.array : join;
289 		import std.format : format;
290 		import std.meta : staticMap;
291 		import std.range : iota;
292 
293 		enum identifierAt(int n) = __traits(identifier, S.tupleof[n]);
294 		enum names = [staticMap!(identifierAt, RangeTuple!(S.tupleof.length))];
295 
296 		return
297 			"S structFun(\n" ~
298 			S.tupleof.length.iota.map!(n =>
299 			"	typeof(S.init.tupleof[%d]) %s = S.init.tupleof[%d],\n".format(n, names[n], n)
300 			).join() ~
301 			`) { return S(` ~ names.join(", ") ~ "); }";
302 	}
303 
304 	mixin(gen());
305 }
306 
307 unittest
308 {
309 	static struct Test
310 	{
311 		string a;
312 		int b = 42;
313 	}
314 
315 	Test test = structFun!Test("banana");
316 	assert(test.a is "banana");
317 	assert(test.b == 42);
318 }
319 
320 /// Evaluate all arguments and return the last argument.
321 /// Can be used instead of the comma operator.
322 /// Inspired by http://clhs.lisp.se/Body/s_progn.htm
323 Args[$-1] progn(Args...)(lazy Args args)
324 {
325 	foreach (n; RangeTuple!(Args.length-1))
326 		cast(void)args[n];
327 	return args[$-1];
328 }
329 
330 unittest
331 {
332 	// Test that expressions are correctly evaluated exactly once.
333 	int a, b, c, d;
334 	d = progn(a++, b++, c++);
335 	assert(a==1 && b==1 && c == 1 && d == 0);
336 	d = progn(a++, b++, ++c);
337 	assert(a==2 && b==2 && c == 2 && d == 2);
338 }
339 
340 unittest
341 {
342 	// Test void expressions.
343 	int a, b;
344 	void incA() { a++; }
345 	void incB() { b++; }
346 	progn(incA(), incB());
347 	assert(a == 1 && b == 1);
348 }
349 
350 /// Like progn, but return the first argument instead.
351 Args[0] prog1(Args...)(lazy Args args)
352 {
353 	auto result = args[0];
354 	foreach (n; RangeTuple!(Args.length-1))
355 		cast(void)args[1+n];
356 	return result;
357 }
358 
359 unittest
360 {
361 	int a = 10, b = 20, c = 30;
362 	int d = prog1(a++, b++, c++);
363 	assert(a==11 && b==21 && c == 31 && d == 10);
364 }
365 
366 // ************************************************************************
367 
368 // Using a compiler with UDA support?
369 enum HAVE_UDA = __traits(compiles, __traits(getAttributes, Object));
370 
371 static if (HAVE_UDA)
372 {
373 	/*
374 	template hasAttribute(T, alias D)
375 	{
376 		enum bool hasAttribute = isValueOfTypeInTuple!(T, __traits(getAttributes, D));
377 	}
378 	*/
379 
380 	/// Detects types and values of the given type
381 	template hasAttribute(Args...)
382 		if (Args.length == 2)
383 	{
384 	//	alias attribute = Args[0];
385 	//	alias symbol = Args[1];
386 
387 		import std.typetuple : staticIndexOf;
388 		import std.traits : staticMap;
389 
390 		static if (is(Args[0]))
391 		{
392 			template isTypeOrValueInTuple(T, Args...)
393 			{
394 				static if (!Args.length)
395 					enum isTypeOrValueInTuple = false;
396 				else
397 				static if (is(Args[0] == T))
398 					enum isTypeOrValueInTuple = true;
399 				else
400 				static if (is(typeof(Args[0]) == T))
401 					enum isTypeOrValueInTuple = true;
402 				else
403 					enum isTypeOrValueInTuple = isTypeOrValueInTuple!(T, Args[1..$]);
404 			}
405 
406 			enum bool hasAttribute = isTypeOrValueInTuple!(Args[0], __traits(getAttributes, Args[1]));
407 		}
408 		else
409 			enum bool hasAttribute = staticIndexOf!(Args[0], __traits(getAttributes, Args[1])) != -1;
410 	}
411 
412 	template getAttribute(T, alias D)
413 	{
414 		enum T getAttribute = findValueOfTypeInTuple!(T, __traits(getAttributes, D));
415 	}
416 
417 	unittest
418 	{
419 		struct Attr { int i; }
420 
421 		struct S
422 		{
423 			@Attr int a;
424 			@Attr(5) int b;
425 			@("test") int c;
426 		}
427 
428 		static assert(hasAttribute!(Attr, S.a));
429 		static assert(hasAttribute!(Attr, S.b));
430 		static assert(hasAttribute!(string, S.c));
431 		static assert(hasAttribute!("test", S.c));
432 	}
433 }
434 else
435 {
436 	template hasAttribute(T, alias D)
437 	{
438 		enum bool hasAttribute = false;
439 	}
440 
441 	template getAttribute(T, alias D)
442 	{
443 		static assert(false, "This D compiler has no UDA support.");
444 	}
445 }
446 
447 // ************************************************************************
448 
449 /// Generate constructors that simply call the parent class constructors.
450 /// Based on http://forum.dlang.org/post/i3hpj0$2vc6$1@digitalmars.com
451 mixin template GenerateConstructorProxies()
452 {
453 	mixin(() {
454 		import std.conv : text;
455 		import std.string : join;
456 		import std.traits : ParameterTypeTuple, fullyQualifiedName;
457 
458 		alias T = typeof(super);
459 
460 		string s;
461 		static if (__traits(hasMember, T, "__ctor"))
462 			foreach (ctor; __traits(getOverloads, T, "__ctor"))
463 			{
464 				string[] declarationList, usageList;
465 				foreach (i, param; ParameterTypeTuple!(typeof(&ctor)))
466 				{
467 					auto varName = "v" ~ text(i);
468 					declarationList ~= fullyQualifiedName!param ~ " " ~ varName;
469 					usageList ~= varName;
470 				}
471 				s ~= "this(" ~ declarationList.join(", ") ~ ") { super(" ~ usageList.join(", ") ~ "); }\n";
472 			}
473 		return s;
474 	} ());
475 }
476 
477 deprecated alias GenerateContructorProxies = GenerateConstructorProxies;
478 
479 unittest
480 {
481 	class A
482 	{
483 		int i, j;
484 		this() { }
485 		this(int i) { this.i = i; }
486 		this(int i, int j ) { this.i = i; this.j = j; }
487 	}
488 
489 	class B : A
490 	{
491 		mixin GenerateConstructorProxies;
492 	}
493 
494 	A a;
495 
496 	a = new B();
497 	assert(a.i == 0);
498 	a = new B(17);
499 	assert(a.i == 17);
500 	a = new B(17, 42);
501 	assert(a.j == 42);
502 }
503 
504 // ************************************************************************
505 
506 /// Generate a @property function which creates/returns
507 /// a thread-local singleton of a class with the given arguments.
508 
509 @property T singleton(T, args...)()
510 	if (is(typeof(new T(args))))
511 {
512 	static T instance;
513 	if (!instance)
514 		instance = new T(args);
515 	return instance;
516 }
517 
518 unittest
519 {
520 	static class C
521 	{
522 		static int n = 0;
523 
524 		this()      { n++; }
525 		this(int x) { n += x; }
526 
527 		void fun() {}
528 	}
529 
530 	alias singleton!C c0;
531 	c0.fun();
532 	c0.fun();
533 	assert(C.n == 1);
534 
535 	alias singleton!(C, 5) c1;
536 	c1.fun();
537 	c1.fun();
538 	assert(C.n == 6);
539 }
540 
541 // ************************************************************************
542 
543 /// Were we built with -debug?
544 debug
545 	enum isDebug = true;
546 else
547 	enum isDebug = false;
548 
549 deprecated alias IsDebug = isDebug;
550 
551 /// Is a specific version on?
552 template isVersion(string versionName)
553 {
554 	mixin(`version (` ~ versionName ~ `) enum isVersion = true; else enum isVersion = false;`);
555 }
556 
557 // ************************************************************************
558 
559 /// Identity function.
560 auto ref T identity(T)(auto ref T value) { return value; }
561 
562 /// Shorter synonym for std.traits.Identity.
563 /// Can be used to UFCS-chain static methods and nested functions.
564 alias I(alias A) = A;
565 
566 // ************************************************************************
567 
568 /// Get f's ancestor which represents its "this" pointer.
569 /// Skips template and mixin ancestors until it finds a struct or class.
570 template thisOf(alias f)
571 {
572 	alias p = Identity!(__traits(parent, f));
573 	static if (is(p == class) || is(p == struct) || is(p == union))
574 		alias thisOf = p;
575 	else
576 		alias thisOf = thisOf!p;
577 }
578 
579 // ************************************************************************
580 
581 /// Return the number of bits used to store the value part, i.e.
582 /// T.sizeof*8 for integer parts and the mantissa size for
583 /// floating-point types.
584 template valueBits(T)
585 {
586 	static if (is(T : ulong))
587 		enum valueBits = T.sizeof * 8;
588 	else
589 	static if (is(T : real))
590 		enum valueBits = T.mant_dig;
591 	else
592 		static assert(false, "Don't know how many value bits there are in " ~ T.stringof);
593 }
594 
595 static assert(valueBits!uint == 32);
596 static assert(valueBits!double == 53);
597 
598 /// Expand to a built-in numeric type of the same kind
599 /// (signed integer / unsigned integer / floating-point)
600 /// with at least the indicated number of bits of precision.
601 template ResizeNumericType(T, uint bits)
602 {
603 	static if (is(T : ulong))
604 		static if (isSigned!T)
605 			alias ResizeNumericType = SignedBitsType!bits;
606 		else
607 			alias ResizeNumericType = UnsignedBitsType!bits;
608 	else
609 	static if (is(T : real))
610 	{
611 		static if (bits <= float.mant_dig)
612 			alias ResizeNumericType = float;
613 		else
614 		static if (bits <= double.mant_dig)
615 			alias ResizeNumericType = double;
616 		else
617 		static if (bits <= real.mant_dig)
618 			alias ResizeNumericType = real;
619 		else
620 			static assert(0, "No floating-point type big enough to fit " ~ bits.stringof ~ " bits");
621 	}
622 	else
623 		static assert(false, "Don't know how to resize type: " ~ T.stringof);
624 }
625 
626 static assert(is(ResizeNumericType!(float, double.mant_dig) == double));
627 
628 /// Expand to a built-in numeric type of the same kind
629 /// (signed integer / unsigned integer / floating-point)
630 /// with at least additionalBits more bits of precision.
631 alias ExpandNumericType(T, uint additionalBits) =
632 	ResizeNumericType!(T, valueBits!T + additionalBits);
633 
634 /// Like ExpandNumericType, but do not error if the resulting type is
635 /// too large to fit any native D type - just expand to the largest
636 /// type of the same kind instead.
637 template TryExpandNumericType(T, uint additionalBits)
638 {
639 	static if (is(typeof(ExpandNumericType!(T, additionalBits))))
640 		alias TryExpandNumericType = ExpandNumericType!(T, additionalBits);
641 	else
642 		static if (is(T : ulong))
643 			static if (isSigned!T)
644 				alias TryExpandNumericType = long;
645 			else
646 				alias TryExpandNumericType = ulong;
647 		else
648 		static if (is(T : real))
649 			alias TryExpandNumericType = real;
650 		else
651 			static assert(false, "Don't know how to expand type: " ~ T.stringof);
652 }
653 
654 /// Unsigned integer type big enough to fit N bits of precision.
655 template UnsignedBitsType(uint bits)
656 {
657 	static if (bits <= 8)
658 		alias ubyte UnsignedBitsType;
659 	else
660 	static if (bits <= 16)
661 		alias ushort UnsignedBitsType;
662 	else
663 	static if (bits <= 32)
664 		alias uint UnsignedBitsType;
665 	else
666 	static if (bits <= 64)
667 		alias ulong UnsignedBitsType;
668 	else
669 		static assert(0, "No integer type big enough to fit " ~ bits.stringof ~ " bits");
670 }
671 
672 template SignedBitsType(uint bits)
673 {
674 	alias Signed!(UnsignedBitsType!bits) SignedBitsType;
675 }
676 
677 /// Evaluates to array of strings with name for each field.
678 @property string[] structFields(T)()
679 	if (is(T == struct) || is(T == class))
680 {
681 	import std.string : split;
682 
683 	string[] fields;
684 	foreach (i, f; T.init.tupleof)
685 	{
686 		string field = T.tupleof[i].stringof;
687 		field = field.split(".")[$-1];
688 		fields ~= field;
689 	}
690 	return fields;
691 }
692 
693 /// Returns the class's initializer instance.
694 /// Returns null if all class fields are zero.
695 /// Can be used to get the value of class fields' initial values.
696 immutable(T) classInit(T)()
697 if (is(T == class))
698 {
699 	return cast(immutable(T))typeid(T).initializer.ptr;
700 }
701 
702 ///
703 unittest
704 {
705 	class C { int n = 42; }
706 	assert(classInit!C.n == 42);
707 }
708 
709 /// Create a functor value type (bound struct) from an alias.
710 template functor(alias fun)
711 {
712 	struct Functor
713 	{
714 		//alias opCall = fun;
715 		auto opCall(T...)(auto ref T args) { return fun(args); }
716 	}
717 
718 	Functor functor()
719 	{
720 		Functor f;
721 		return f;
722 	}
723 }
724 
725 static if (haveAliasStructBinding)
726 unittest
727 {
728 	static void caller(F)(F fun)
729 	{
730 		fun(42);
731 	}
732 
733 	int result;
734 	caller(functor!((int i) => result = i));
735 	assert(result == 42);
736 }