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