1 /** 2 Provides meta utilities that can modify types 3 */ 4 module bolts.meta; 5 6 import bolts.internal; 7 8 /** 9 Flattens a list of types to their `ElementType` or in the case of an AliasPack it's `.UnpackDeep` 10 11 If a type is a range then its `ElementType` is used 12 */ 13 template Flatten(Values...) { 14 import std.meta: AliasSeq; 15 static if (Values.length) { 16 import std.range: isInputRange; 17 import std.traits: isInstanceOf; 18 19 alias Head = Values[0]; 20 alias Tail = Values[1..$]; 21 22 static if (isInstanceOf!(AliasPack, Head)) { 23 alias Flatten = Flatten!(Head.UnpackDeep, Flatten!Tail); 24 } else static if (isInputRange!Head) { 25 import std.range: ElementType; 26 alias Flatten = Flatten!(ElementType!Head, Flatten!Tail); 27 } else { 28 alias Flatten = AliasSeq!(Head, Flatten!Tail); 29 } 30 } else { 31 alias Flatten = AliasSeq!(); 32 } 33 } 34 35 /// 36 unittest { 37 import std.algorithm: filter; 38 import std.meta: AliasSeq; 39 40 alias R1 = typeof([1, 2, 3].filter!"true"); 41 alias R2 = typeof([1.0, 2.0, 3.0]); 42 43 static assert(is(Flatten!(int, double) == AliasSeq!(int, double))); 44 static assert(is(Flatten!(int, R1, R2) == AliasSeq!(int, int, double))); 45 static assert(is(Flatten!(R1, int) == AliasSeq!(int, int))); 46 47 import std.traits: CommonType; 48 static assert(is(CommonType!(Flatten!(int, R1, R2, float)) == double)); 49 50 static assert(is(Flatten!(R1, int, AliasPack!(AliasPack!(float, int)), int) == AliasSeq!(int, int, float, int, int))); 51 } 52 53 /** 54 Same as an AliasSeq that does not auto expand. 55 56 You can get to the provided compile time sequence (AliasSeq) by accessing the `.Unpack` member. 57 And if you want a recursive expansion there's `UnpackDeep` for that. Also a convenience 58 `.equals!(otherAliasPack)` is provided. 59 60 See_Also: 61 - https://forum.dlang.org/post/mnobngrzdmqbxomulpts@forum.dlang.org 62 */ 63 template AliasPack(T...) { 64 alias Unpack = T; 65 enum length = Unpack.length; 66 67 private template UnpackDeepImpl(U...) { 68 import std.meta: AliasSeq; 69 static if (U.length) { 70 import std.traits: isInstanceOf; 71 static if (isInstanceOf!(AliasPack, U[0])) { 72 alias Head = UnpackDeepImpl!(U[0].Unpack); 73 } else { 74 import std.meta: Alias; 75 alias Head = Alias!(U[0]); 76 } 77 alias UnpackDeepImpl = AliasSeq!(Head, UnpackDeepImpl!(U[1 .. $])); 78 } else { 79 alias UnpackDeepImpl = AliasSeq!(); 80 } 81 } 82 83 alias UnpackDeep = UnpackDeepImpl!T; 84 85 template equals(U...) { 86 static if (T.length == U.length) { 87 static if (T.length == 0) { 88 enum equals = true; 89 } else { 90 import bolts.traits: isSame; 91 enum equals = isSame!(T[0], U[0]) && AliasPack!(T[1 .. $]).equals!(U[1 .. $]); 92 } 93 } else { 94 enum equals = false; 95 } 96 } 97 98 static if (length > 0) { 99 import std.meta: Alias; 100 alias Head = Alias!(T[0]); 101 } else { 102 alias Head = void; 103 } 104 105 static if (length > 1) { 106 alias Tail = AliasPack!(T[1 .. $]); 107 } else { 108 alias Tail = AliasPack!(); 109 } 110 } 111 112 /// 113 unittest { 114 alias P = AliasPack!(1, int, "abc"); 115 static assert( P.equals!(1, int, "abc")); 116 static assert(!P.equals!(1, int, "cba")); 117 118 static assert(P.Head == 1); 119 static assert(P.Tail.equals!(int, "abc")); 120 static assert(is(P.Tail.Head == int)); 121 static assert(P.Tail.Tail.Head == "abc"); 122 123 alias PackOfPacks = AliasPack!(AliasPack!(1, int), AliasPack!(2, float), 17); 124 static assert(AliasPack!(PackOfPacks.UnpackDeep).equals!(1, int, 2, float, 17)); 125 } 126 127 deprecated("use Zip instead") 128 alias staticZip = Zip; 129 130 /** 131 Zips sequences of `AliasPack`s together into an AliasPack of AliasPacks. 132 133 See_Also: 134 - https://forum.dlang.org/post/mnobngrzdmqbxomulpts@forum.dlang.org 135 */ 136 template Zip(Seqs...) { 137 import std.traits: isInstanceOf; 138 import std.meta: allSatisfy, staticMap; 139 140 private enum isPack(alias T) = isInstanceOf!(AliasPack, T); 141 142 static assert( 143 Seqs.length >= 2 && allSatisfy!(isPack, Seqs), 144 "Must have 2 or more arguments of type AliasPack" 145 ); 146 147 enum len = Seqs[0].length; 148 static foreach (Seq; Seqs[1 .. $]) { 149 static assert( 150 Seq.length == len, 151 "All arguments to Zip must have the same length" 152 ); 153 } 154 155 alias Head(alias P) = P.Head; 156 alias Tail(alias P) = P.Tail; 157 158 static if (len == 0) { 159 alias Zip = AliasPack!(); 160 } else { 161 alias Zip = AliasPack!( 162 AliasPack!(staticMap!(Head, Seqs)), 163 Zip!(staticMap!(Tail, Seqs)).Unpack 164 ); 165 } 166 } 167 168 /// 169 unittest { 170 alias a = AliasPack!(1, 2, 3); 171 alias b = AliasPack!(4, 5, 6); 172 alias c = AliasPack!(7, 8, 9); 173 alias d = Zip!(a, b, c); 174 175 static assert(d.length == 3); 176 177 static assert(d.Unpack[0].equals!(1, 4, 7)); 178 static assert(d.Unpack[1].equals!(2, 5, 8)); 179 static assert(d.Unpack[2].equals!(3, 6, 9)); 180 } 181 182 /** 183 Extract the elements of an `AliasPack` at given positions. 184 185 The function takes two parameters - the first is the `AliasPack` to extract 186 from, the second is an `AliasPack` of positions. 187 */ 188 template Pluck(alias Pack, size_t[] Positions) if (from.std.traits.isInstanceOf!(AliasPack, Pack)) { 189 static if (Positions.length == 0) { 190 alias Pluck = AliasPack!(); 191 } else { 192 static assert( 193 Positions[0] >= 0 && Positions[0] < Pack.length, 194 "Position is out of range" 195 ); 196 alias Pluck = AliasPack!( 197 Pack.Unpack[Positions[0]], 198 Pluck!(Pack, Positions[1..$]).Unpack 199 ); 200 } 201 } 202 203 /// 204 unittest { 205 static assert(Pluck!(AliasPack!(), []).equals!()); 206 static assert(Pluck!(AliasPack!(int, char, float), []).equals!()); 207 static assert(Pluck!(AliasPack!(int, char, float), [0, 2]).equals!(int, float)); 208 } 209 210 /** 211 Filters all the members of a type based on a provided predicate 212 213 The predicate takes two parameters - the first is the type, the second is 214 the string name of the member being iterated on. 215 */ 216 template FilterMembersOf(T, alias Fn) { 217 import std.meta: Filter, ApplyLeft; 218 alias ResolvedType = ResolvePointer!T; 219 alias FilterMembersOf = Filter!(ApplyLeft!(Fn, ResolvedType), __traits(allMembers, ResolvedType)); 220 } 221 222 /// 223 unittest { 224 import std.meta: AliasSeq; 225 226 struct S { 227 int i; 228 float f; 229 int i2; 230 short s; 231 } 232 233 enum hasInt(T, string name) = is(typeof(__traits(getMember, T, name)) == int); 234 static assert(FilterMembersOf!(S, hasInt) == AliasSeq!("i", "i2")); 235 } 236 237 /** 238 Removes all the attributes from compile-time entity that is given as the only argument 239 */ 240 template RemoveAttributes(T...) { 241 import std.traits: functionLinkage, SetFunctionAttributes, FunctionAttribute; 242 alias U = from.bolts.traits.TypesOf!T[0]; 243 alias RemoveAttributes = SetFunctionAttributes!(U, functionLinkage!U, FunctionAttribute.none); 244 } 245 246 /// 247 unittest { 248 void f0() @safe {} 249 void f1() @system {} 250 251 static assert(is(RemoveAttributes!f1 == RemoveAttributes!f0)); 252 }