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