1 /** Various metaprogramming helpers.
2  *
3  *  Authors: $(LINK2 https://github.com/epi, Adrian Matoga)
4  *  Copyright: © 2016 Adrian Matoga
5  *  License: $(LINK2 http://www.boost.org/users/license.html, BSL-1.0).
6  */
7 module flod.meta;
8 
9 package:
10 
11 /// Used to compare alias lists.
12 struct Id(X...) {}
13 
14 /// Tests if template `S` can be instantiated with argument list `A` and the instantiation is a type.
15 /// Workaround for https://issues.dlang.org/show_bug.cgi?id=15623
16 template isType(alias T, A...) {
17 	bool impl() { return is(T!A); }
18 	enum isType = impl();
19 }
20 
21 unittest {
22 	struct NoFoo {}
23 
24 	struct HasFoo {
25 		void foo() {}
26 	}
27 
28 	struct CallsFoo(T) {
29 		T t;
30 		void run() { t.foo(); }
31 	}
32 
33 	static assert(!isType!(CallsFoo, NoFoo));
34 	static assert( isType!(CallsFoo, HasFoo));
35 }
36 
37 ///
38 template str(W...) {
39 	static if (W.length == 0) {
40 		enum str = "()";
41 	} else static if (W.length > 1) {
42 		enum str = str!(W[0]) ~ "," ~ str!(W[1 .. $]);
43 	} else {
44 		import std.traits : isExpressions;
45 		static if (isExpressions!(W[0])) {
46 			import std.conv : to;
47 			enum str = to!string(W[0]);
48 		} else {
49 			import std.traits : fullyQualifiedName;
50 			alias V = W[0];
51 			static if (is(typeof(V.str) : string))
52 				enum str = V.str;
53 			else static if (__traits(compiles, fullyQualifiedName!V))
54 				enum str = fullyQualifiedName!V;
55 			else static if (__traits(compiles, __traits(identifier, V)))
56 				enum str = __traits(identifier, V);
57 			else
58 				enum str = V.stringof;
59 		}
60 	}
61 }
62 
63 ///
64 template ReplaceWithMask(ulong mask, ReplacementForZeros, Types...) {
65 	alias What = ReplacementForZeros;
66 	import std.meta : AliasSeq;
67 	static if (Types.length == 0)
68 		alias ReplaceWithMask = AliasSeq!();
69 	else {
70 		static if (mask & 1)
71 			alias ReplaceWithMask = AliasSeq!(ReplaceWithMask!(mask >> 1, What, Types[0 .. $ - 1]), Types[$ - 1]);
72 		else
73 			alias ReplaceWithMask = AliasSeq!(ReplaceWithMask!(mask >> 1, What, Types[0 .. $ - 1]), What);
74 	}
75 }
76 
77 unittest {
78 	static struct Empty {}
79 	struct Z(Params...) {}
80 	alias List = ReplaceWithMask!(0b011011, Empty, int, bool, float, uint, ulong, double);
81 	static assert(is(Z!List == Z!(Empty, bool, float, Empty, ulong, double)));
82 }
83 
84 /// Mix it in inside a `struct` definition to make the `struct` non-copyable.
85 mixin template NonCopyable() {
86 	@disable this(this);
87 	@disable void opAssign(typeof(this));
88 }
89 
90 /// Evaluates to true iff instances of `T` can be copied.
91 template isCopyable(T) {
92 	enum isCopyable = is(typeof({ T a; T b = a; T c = b; }));
93 }
94 
95 unittest {
96 	static struct A {}
97 	static struct B { @disable this(this); }
98 	static assert( isCopyable!A);
99 	static assert(!isCopyable!B);
100 }
101 
102 /// Forwards to `std.algorithm.move` iff `t` is non-copyable.
103 auto moveIfNonCopyable(T, string file = __FILE__, int line = __LINE__)(auto ref T t)
104 {
105 	import std.conv : to;
106 	static if (isCopyable!T) {
107 		return t;
108 	} else {
109 		import std.algorithm : move;
110 		return move(t);
111 	}
112 }
113 
114 template tupleFromArray(T, T[] arr) {
115 	import std.meta : AliasSeq;
116 	static if (arr.length)
117 		alias tupleFromArray = AliasSeq!(arr[0], tupleFromArray!(T, arr[1 .. $]));
118 	else
119 		alias tupleFromArray = AliasSeq!();
120 }