Mercurial > projects > qtd
comparison d2/qtd/meta/Compiletime.d @ 351:59d847a814e3
added meta subdir
author | Max Samukha <maxter@spambox.com> |
---|---|
date | Thu, 20 May 2010 15:54:06 +0300 |
parents | |
children | 9784459f0750 |
comparison
equal
deleted
inserted
replaced
350:31520b2c0b3c | 351:59d847a814e3 |
---|---|
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 } |