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 }