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