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 }