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