351
|
1 /**************************************************************************
|
|
2 Copyright: QtD Team, 2010
|
|
3 Authors: Max Samukha, Eldar Insafutdinov
|
|
4 License: Boost Software License 1.0
|
|
5 **************************************************************************/
|
|
6 module qtd.meta.Compiletime;
|
|
7
|
|
8 import
|
|
9 std.traits,
|
|
10 std.conv,
|
|
11 std.variant,
|
|
12 std.typetuple;
|
|
13
|
|
14 import std.string : startsWith;
|
|
15
|
|
16 /*
|
|
17 uint startsWith(string s, string pattern)
|
|
18 {
|
|
19 if (pattern.length <= s.length && s[0..pattern.length] == pattern)
|
|
20 return pattern.length;
|
|
21
|
|
22 return 0;
|
|
23 }
|
|
24 */
|
|
25
|
|
26
|
|
27 enum standardNamespace = "qtd";
|
|
28
|
|
29 template Alias(A...) if (A.length == 1)
|
|
30 {
|
|
31 static if (__traits(compiles, { alias A[0] x; }))
|
|
32 alias A[0] Alias;
|
|
33 else
|
|
34 enum Alias = A[0];
|
|
35 }
|
|
36
|
|
37 /**
|
|
38 Encapsulates a static tuple. Useful for preventing tuples from
|
|
39 flattening when they are passed to a variadic template.
|
|
40 */
|
|
41 template TypeTupleWrapper(A...)
|
|
42 {
|
|
43 alias A tuple;
|
|
44 }
|
|
45
|
|
46 // returns the type of a template parameter if there is one
|
|
47 template templateParam(U : V!(U), alias V)
|
|
48 {
|
|
49 alias U templateParam;
|
|
50 }
|
|
51
|
|
52
|
|
53 /**
|
|
54 Returns a tuple with T repeated count times.
|
|
55 */
|
|
56 // TODO: generalize to accept any symbol
|
|
57 template Repeat(T, size_t count)
|
|
58 {
|
|
59 static if (count)
|
|
60 alias TypeTuple!(count, Repeat!(T, count - 1)) Repeat;
|
|
61 else
|
|
62 alias TypeTuple!() Repeat;
|
|
63 }
|
|
64
|
|
65 /**
|
|
66 Returns the required number of function arguments, optional arguments excluded
|
|
67 */
|
|
68 int requiredArgCount(alias fn)() {
|
|
69 alias ParameterTypeTuple!(typeof(&fn)) P;
|
|
70 P p;
|
|
71 static if (P.length == 0)
|
|
72 return 0;
|
|
73
|
|
74 foreach(i, _; P)
|
|
75 {
|
|
76 static if (!__traits(compiles, fn(p[0..$-i-1])))
|
|
77 {
|
|
78 return p.length - i;
|
|
79 }
|
|
80 }
|
|
81 return 0;
|
|
82 }
|
|
83
|
|
84 /**
|
|
85 Returns true if is a delegate type.
|
|
86 */
|
|
87 template isDelegate(T)
|
|
88 {
|
|
89 enum isDelegate = is(T == delegate);
|
|
90 }
|
|
91
|
|
92 /**
|
|
93 Returns true if T is a function pointer type.
|
|
94 */
|
|
95 template isFunctionPointer(T)
|
|
96 {
|
|
97 enum isFuntionPointer = is(typeof(*T.init) == function);
|
|
98 }
|
|
99
|
|
100 /**
|
|
101 Returns true if entity is a function or function type.
|
|
102 */
|
|
103 template isFunction(alias entity)
|
|
104 {
|
|
105 static if (is(entity))
|
|
106 alias isFunction!(entity, void) isFunction;
|
|
107 else
|
|
108 enum isFunction = is(typeof(entity) == function);
|
|
109 }
|
|
110
|
|
111 template isFunction(T, Dummy = void)
|
|
112 {
|
|
113 enum isFunction = is(T == function);
|
|
114 }
|
|
115
|
|
116
|
|
117 //TODO: hack
|
|
118 uint isModule(string str)
|
|
119 {
|
|
120 return startsWith(str, "module ");
|
|
121 }
|
|
122
|
|
123
|
|
124 template qualifiedCppName(T)
|
|
125 {
|
|
126 static if(!isModule(__traits(parent, T).stringof))
|
|
127 enum qualifiedCppName = qualifiedCppName!(__traits(parent, T)) ~ "::" ~ T.stringof;
|
|
128 else
|
|
129 enum qualifiedCppName = T.stringof;
|
|
130 }
|
|
131
|
|
132 template qualifiedName(T)
|
|
133 {
|
|
134 static if (!isModule(__traits(parent, T).stringof))
|
|
135 enum qualifiedName = qualifiedName!(__traits(parent, T)) ~ "." ~ T.stringof;
|
|
136 else
|
|
137 enum qualifiedName = T.stringof;
|
|
138 }
|
|
139
|
|
140 template fullName(T)
|
|
141 {
|
|
142 static if (is(T == enum))
|
|
143 enum fullName = qualifiedName!T;
|
|
144 else
|
|
145 enum fullName = T.stringof;
|
|
146 }
|
|
147
|
|
148 /**
|
|
149 */
|
|
150 enum AttributeOptions
|
|
151 {
|
|
152 /**
|
|
153 */
|
|
154 none,
|
|
155
|
|
156 /**
|
|
157 Allows multiple equally named attributes to be associated with the symbol.
|
|
158 */
|
|
159 allowMultiple = 0x0000_0001,
|
|
160
|
|
161 /* internal */ inner = 0x0000_0002,
|
|
162 }
|
|
163
|
|
164 /**
|
|
165 When mixed in an aggregate, converts a compile-time tuple to
|
|
166 members of that aggregate.
|
|
167 */
|
|
168 mixin template tupleToMembers(string nameSpace, size_t index, A...)
|
|
169 {
|
|
170 static if (index < A.length)
|
|
171 {
|
|
172 enum indexStr = to!string(index);
|
|
173
|
|
174 static if (is(typeof( { typeof(A[index]) x; }() )))
|
|
175 mixin("typeof(A[" ~ indexStr ~ "]) " ~ nameSpace ~ indexStr ~ " = A[" ~ indexStr ~"];\n");
|
|
176 else
|
|
177 mixin("alias Alias!(A[" ~ indexStr ~ "]) " ~ nameSpace ~ indexStr ~ ";\n");
|
|
178
|
|
179 mixin tupleToMembers!(nameSpace, index + 1, A);
|
|
180 }
|
|
181 }
|
|
182
|
|
183 /**
|
|
184 When mixed in an aggregate, converts a compile-time tuple of name-value pairs to
|
|
185 members of that aggregate.
|
|
186 */
|
|
187 mixin template NameValueTupleToFields(A...)
|
|
188 {
|
|
189
|
|
190 }
|
|
191
|
|
192 version (QtdUnittest)
|
|
193 {
|
|
194 unittest
|
|
195 {
|
|
196 static int foo()
|
|
197 {
|
|
198 return 42;
|
|
199 }
|
|
200
|
|
201 static struct S
|
|
202 {
|
|
203 mixin tupleToMembers!("member", 0,
|
|
204 int,
|
|
205 "a",
|
|
206 22,
|
|
207 foo);
|
|
208 }
|
|
209
|
|
210 static assert(is(S.member0 == int));
|
|
211 S s;
|
|
212 assert(s.member1 == "a");
|
|
213 assert(s.member2 == 22);
|
|
214 assert(S.member3() == 42);
|
|
215 }
|
|
216 }
|
|
217
|
|
218 private template attributeId(alias symbol, uint index = 0)
|
|
219 {
|
|
220 enum attributeId = standardNamespace ~ "_attr_" ~ uniqueId!symbol ~ "_" ~ to!string(index);
|
|
221 }
|
|
222
|
|
223 /**
|
|
224 Attributes allow to associate arbitrary compile-time data
|
|
225 with a declaration and optionaly make that data available at run-time.
|
|
226
|
|
227 ----
|
|
228 class A
|
|
229 {
|
|
230 }
|
|
231 mixin Attribute!(C, "someAttribute", "B");
|
|
232 ----
|
|
233
|
|
234 The example above associates the string "B" with class A under the attribute name "someAttribute".
|
|
235 Multiple attributes with the same name can be associated with a single declaration.
|
|
236
|
|
237 ----
|
|
238 class A
|
|
239 {
|
|
240 }
|
|
241 mixin Attribute!(C, "someAttribute", "B");
|
|
242 mixin Attribute!(C, "someAttribute", AttributeOptions.allowMultiple, "C");
|
|
243 ----
|
|
244
|
|
245 Attributes can be accessed at compile-time as follows:
|
|
246
|
|
247 ----
|
|
248 alias GetAttributes!(C, "someAttribute") attrs;
|
|
249 ----
|
|
250
|
|
251 GetAttribute returns a static tuple, which contains "someAttribute" attributes in the form
|
|
252 of TypeTupleWrapper instances. The wrapped tuples have the following layout:
|
|
253
|
|
254 [0] - attribute name
|
|
255 [1] - attribute options
|
|
256 [2..$] - attribute data
|
|
257
|
|
258 ----
|
|
259 alias attrs[0] attr0;
|
|
260 alias attrs[1] attr1;
|
|
261
|
|
262 static assert(attrs0.tuple[0] == "someAttribute" && attrs0.tuple[2] == "B");
|
|
263 static assert(attrs1.tuple[0] == "someAttribute" && attrs1.tuple[2] == "C");
|
|
264 ----
|
|
265
|
|
266 Attributes can be inserted inside the body of a declaration.
|
|
267 ----
|
|
268 class A
|
|
269 {
|
|
270 mixin Attribute!("someAttribute", "B");
|
|
271 }
|
|
272 ----
|
|
273
|
|
274 Attributes can be made available at run time by means of the declaration's meta-object
|
|
275 ----
|
|
276 // prints names of all attributes associated with class A
|
|
277 auto a = new A;
|
|
278 foreach (attr; a.metaObject.attributes)
|
|
279 writeln(attr.name);
|
|
280 ----
|
|
281
|
|
282 Attributes can be specialized
|
|
283
|
|
284 ----
|
|
285 mixin template DbFieldAttribute(alias prop, string columnName)
|
|
286 {
|
|
287 mixin Attribute!(prop, "DbFieldAttribute", columnName);
|
|
288 }
|
|
289
|
|
290 class A
|
|
291 {
|
|
292 int value;
|
|
293 mixin DbFieldAttribute!(value, "Value");
|
|
294
|
|
295 int anotherValue;
|
|
296 mixin DbFieldAttribute!(anotherValue, "Value2");
|
|
297 }
|
|
298
|
|
299 assert(GetAttributes!(A.value, "DbFieldAttribute")[0].tuple[1] == "Value");
|
|
300 assert(GetAttributes!(A.anotherValue, "DbFieldAttribute")[0].tuple[1] == "Value2");
|
|
301 ----
|
|
302
|
|
303 */
|
|
304 mixin template Attribute(alias symbol, string attrClass, A...)
|
|
305 {
|
|
306 mixin Attribute!(symbol, attrClass, AttributeOptions.none, A);
|
|
307 }
|
|
308
|
|
309 /// ditto
|
|
310 mixin template Attribute(alias symbol, string attrClass, AttributeOptions opts, A...)
|
|
311 {
|
|
312 mixin AttributeImpl!(symbol, attrClass, opts, 0, A);
|
|
313 }
|
|
314
|
|
315
|
|
316 /// ditto
|
|
317 // TODO: probably make this an overload of Attribute
|
|
318 mixin template InnerAttribute(string attrClass, AttributeOptions opts, A...)
|
|
319 {
|
|
320 // BUG: needs to be generalized to accept any parent
|
|
321 mixin Attribute!(typeof(this), attrClass, opts | AttributeOptions.inner, A);
|
|
322 }
|
|
323
|
|
324 /// ditto
|
|
325 mixin template InnerAttribute(string attrClass, A...)
|
|
326 {
|
|
327 mixin InnerAttribute!(attrClass, AttributeOptions.none, A);
|
|
328 }
|
|
329
|
|
330 private mixin template AttributeImpl(alias symbol, string attrClass, AttributeOptions opts, size_t index, A...)
|
|
331 {
|
|
332 private enum attrId = attributeId!(symbol, index) ~ (opts & AttributeOptions.inner ? "_inner" : "");
|
|
333
|
|
334 static if (is(typeof(mixin(attrId))))
|
|
335 {
|
|
336 mixin ("alias " ~ attrId ~ " attr;");
|
|
337 static if (!(opts & AttributeOptions.allowMultiple))
|
|
338 {
|
|
339 static assert (attr[0] != attrClass, "Multiple " ~ attrClass ~ " attributes are not allowed for "
|
|
340 ~ __traits(parent, symbol).stringof ~ "." ~ stringOf!symbol);
|
|
341 }
|
|
342
|
|
343 mixin AttributeImpl!(symbol, attrClass, opts, index + 1, A);
|
|
344 }
|
|
345 else
|
|
346 {
|
|
347 mixin ("alias TypeTuple!(attrClass, opts, A) " ~ attrId ~ ";");
|
|
348 }
|
|
349 }
|
|
350
|
|
351 private string stringOfFunction(alias symbol)()
|
|
352 {
|
|
353 auto ptrType = typeof(&symbol).stringof;
|
|
354 auto paramList = ParameterTypeTuple!(symbol).stringof;
|
|
355
|
|
356 string result = ReturnType!(symbol).stringof ~ " " ~ __traits(identifier, symbol) ~ paramList;
|
|
357
|
|
358 if (ptrType[$ - 1] != ')')
|
|
359 {
|
|
360 for (size_t i = ptrType.length - 2;; --i)
|
|
361 {
|
|
362 if (ptrType[i] == ')')
|
|
363 {
|
|
364 result ~= ptrType[i + 1..$];
|
|
365 break;
|
|
366 }
|
|
367 }
|
|
368 }
|
|
369
|
|
370 return result;
|
|
371 }
|
|
372
|
|
373 /**
|
|
374 String of any symbol, including functions
|
|
375 */
|
|
376 template stringOf(alias symbol)
|
|
377 {
|
|
378 static if (isFunction!symbol)
|
|
379 enum stringOf = stringOfFunction!symbol;
|
|
380 else
|
|
381 enum stringOf = symbol.stringof;
|
|
382 }
|
|
383
|
|
384 /**
|
|
385 Returns the string uniquely identifying the
|
|
386 symbol in its container.
|
|
387 */
|
|
388 template uniqueName(alias symbol)
|
|
389 {
|
|
390 enum uniqueName = stringOf!symbol;
|
|
391 }
|
|
392
|
|
393 // TODO: works only for simple types. implement
|
|
394 string uniqueIdImpl(string symbol)
|
|
395 {
|
|
396 char[] r = symbol.dup;
|
|
397 foreach (i, c; symbol)
|
|
398 {
|
|
399 if (c == '(' || c == ')' || c == ' ')
|
|
400 r[i] = '_';
|
|
401 }
|
|
402
|
|
403 return cast(immutable)r;
|
|
404 }
|
|
405
|
|
406 /**
|
|
407 Returns a valid D identifier uniquely
|
|
408 identifying symbol in its container.
|
|
409 */
|
|
410 template uniqueId(alias symbol)
|
|
411 {
|
|
412 enum uniqueId = uniqueIdImpl(stringOf!symbol);
|
|
413 }
|
|
414
|
|
415 version (QtdUnittest)
|
|
416 {
|
|
417 unittest
|
|
418 {
|
|
419 static class C
|
|
420 {
|
|
421 void foo() const {};
|
|
422 bool bar(int) { return true; };
|
|
423 int x;
|
|
424 static assert (stringOf!foo == "void foo() const");
|
|
425 static assert (uniqueName!foo == "void foo() const");
|
|
426 static assert (stringOf!bar == "bool bar(int)");
|
|
427 static assert (uniqueName!bar == "bool bar(int)");
|
|
428 static assert (stringOf!x == "x");
|
|
429 static assert (uniqueName!x == "x");
|
|
430 }
|
|
431
|
|
432 static assert (stringOf!(C.foo) == "void foo() const");
|
|
433 static assert (uniqueName!(C.foo) == "void foo() const");
|
|
434 static assert (stringOf!(C.bar) == "bool bar(int)");
|
|
435 static assert (uniqueName!(C.bar) == "bool bar(int)");
|
|
436
|
|
437 static assert (stringOf!(C.x) == "x");
|
|
438 static assert (uniqueName!(C.x) == "x");
|
|
439 }
|
|
440 }
|
|
441
|
|
442 /**
|
|
443 Predicate that always evaluates to true regardless of arguments.
|
|
444 */
|
|
445 template truePred(A...)
|
|
446 {
|
|
447 enum truePred = true;
|
|
448 }
|
|
449
|
|
450 private template attrNamePred(string name)
|
|
451 {
|
|
452 template attrNamePred(A...)
|
|
453 {
|
|
454 enum attrNamePred = A[0] == name;
|
|
455 }
|
|
456 }
|
|
457
|
|
458 /**
|
|
459 Returns a compile-time tuple of attributes that
|
|
460 match pred.
|
|
461 */
|
|
462 template GetAttributes(alias symbol, alias pred = truePred)
|
|
463 {
|
|
464 alias GetAttributesImpl!(symbol, 0, pred).result GetAttributes;
|
|
465 }
|
|
466
|
|
467 /**
|
|
468 Returns a compile-time tuple of attributes
|
|
469 matching the specified attribute name.
|
|
470 */
|
|
471 template GetAttributes(alias symbol, string attrName)
|
|
472 {
|
|
473 alias GetAttributes!(symbol, attrNamePred!attrName) GetAttributes;
|
|
474 }
|
|
475
|
|
476
|
|
477 template GetAttributesImpl(alias symbol, size_t index, alias pred)
|
|
478 {
|
|
479 //pragma(msg, mixin("symbol." ~ attributeId!(symbol, index) ~ "_inner").stringof);
|
|
480 static if (is(typeof(mixin("__traits(parent, symbol)." ~ attributeId!(symbol, index)))))
|
|
481 mixin ("alias Alias!(__traits(parent, symbol))." ~ attributeId!(symbol, index) ~ " attr;");
|
|
482 else static if (is(typeof(mixin("symbol." ~ attributeId!(symbol, index) ~ "_inner"))))
|
|
483 mixin ("alias symbol." ~ attributeId!(symbol, index) ~ "_inner attr;");
|
|
484
|
|
485 static if (is(typeof(attr)))
|
|
486 {
|
|
487 alias GetAttributesImpl!(symbol, index + 1, pred).result next;
|
|
488
|
|
489 static if (pred!attr)
|
|
490 alias TypeTuple!(TypeTupleWrapper!attr, next) result;
|
|
491 else
|
|
492 alias next result;
|
|
493 }
|
|
494 else
|
|
495 alias TypeTuple!() result;
|
|
496 }
|
|
497
|
|
498 version (QtdUnittest)
|
|
499 {
|
|
500 mixin template MyAttribute(alias symbol, A...)
|
|
501 {
|
|
502 mixin Attribute!(symbol, "MyAttribute", AttributeOptions.allowMultiple, A);
|
|
503 }
|
|
504
|
|
505 mixin template ClassInfo(string name, alias value)
|
|
506 {
|
|
507 mixin InnerAttribute!("ClassInfo", AttributeOptions.allowMultiple, name, value);
|
|
508 }
|
|
509
|
|
510 unittest
|
|
511 {
|
|
512 static class C
|
|
513 {
|
|
514 // inner C attributes
|
|
515 mixin InnerAttribute!("Inner", 33); // generic
|
|
516 mixin ClassInfo!("version", 123);
|
|
517 mixin ClassInfo!("author", "James Bond");
|
|
518
|
|
519
|
|
520 void foo() {};
|
|
521 // foo attributes
|
|
522 mixin Attribute!(foo, "SomeAttribute", 42);
|
|
523 mixin MyAttribute!(foo, 1, 2);
|
|
524 mixin MyAttribute!(foo, 3, 4);
|
|
525
|
|
526 alias GetAttributes!(typeof(this), "Inner") innerAttrs;
|
|
527 static assert(innerAttrs[0].tuple[0] == "Inner");
|
|
528 }
|
|
529 // outer C attribute
|
|
530 mixin MyAttribute!(C, 24);
|
|
531
|
|
532 alias GetAttributes!(C, "Inner") innerAttrs;
|
|
533 static assert(innerAttrs[0].tuple[0] == "Inner" && innerAttrs[0].tuple[2] == 33);
|
|
534
|
|
535 alias GetAttributes!(C, "ClassInfo") ciAttrs;
|
|
536 static assert(ciAttrs[0].tuple[2] == "version" && ciAttrs[0].tuple[3] == 123);
|
|
537
|
|
538 alias GetAttributes!(C.foo, "SomeAttribute") someAttr;
|
|
539 static assert(someAttr.length == 1);
|
|
540 static assert(someAttr[0].tuple[0] == "SomeAttribute");
|
|
541
|
|
542 alias GetAttributes!(C.foo, "MyAttribute") myAttrs;
|
|
543
|
|
544 //COMPILER BUG: cannot 'alias myAttrs[0].tuple myAttrs_0';
|
|
545 static assert(myAttrs[0].tuple[0] == "MyAttribute");
|
|
546 static assert(myAttrs[0].tuple[2] == 1 && myAttrs[0].tuple[3] == 2);
|
|
547
|
|
548 static assert(myAttrs[1].tuple[0] == "MyAttribute");
|
|
549 static assert(myAttrs[1].tuple[2] == 3 && myAttrs[1].tuple[3] == 4);
|
|
550
|
|
551 /+ BUG: Fails: local declarations cannot be accessed as parent.localDecl
|
|
552 alias GetAttributes!(C, "MyAttribute") myAttrs2;
|
|
553 static assert(myAttrs2[0].tuple[0] == "MyAttribute");
|
|
554 static assert(myAttrs2[0].tuple[1] == 24);
|
|
555 +/
|
|
556 }
|
|
557 } |