Mercurial > projects > qtd
annotate d2/qtd/meta/Compiletime.d @ 364:a084e2df3776
Preparing for non-QObject meta-objects. Now meta-objects for static types can be uniformly accessed using meta!T
author | Max Samukha <maxter@maxter.com> |
---|---|
date | Fri, 11 Jun 2010 12:16:09 +0300 |
parents | 9784459f0750 |
children | 958e8b9a89bd |
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 | |
9 std.conv, | |
10 std.variant, | |
11 std.typetuple; | |
12 | |
364
a084e2df3776
Preparing for non-QObject meta-objects. Now meta-objects for static types can be uniformly accessed using meta!T
Max Samukha <maxter@maxter.com>
parents:
357
diff
changeset
|
13 public import std.traits; |
a084e2df3776
Preparing for non-QObject meta-objects. Now meta-objects for static types can be uniformly accessed using meta!T
Max Samukha <maxter@maxter.com>
parents:
357
diff
changeset
|
14 |
351 | 15 import std.string : startsWith; |
16 | |
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
|
17 /** |
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
|
18 */ |
351 | 19 enum standardNamespace = "qtd"; |
20 | |
21 template Alias(A...) if (A.length == 1) | |
22 { | |
23 static if (__traits(compiles, { alias A[0] x; })) | |
24 alias A[0] Alias; | |
25 else | |
26 enum Alias = A[0]; | |
27 } | |
28 | |
29 /** | |
30 Encapsulates a static tuple. Useful for preventing tuples from | |
31 flattening when they are passed to a variadic template. | |
32 */ | |
33 template TypeTupleWrapper(A...) | |
34 { | |
35 alias A tuple; | |
36 } | |
37 | |
38 | |
39 /** | |
40 Returns a tuple with T repeated count times. | |
41 */ | |
42 // TODO: generalize to accept any symbol | |
43 template Repeat(T, size_t count) | |
44 { | |
45 static if (count) | |
46 alias TypeTuple!(count, Repeat!(T, count - 1)) Repeat; | |
47 else | |
48 alias TypeTuple!() Repeat; | |
49 } | |
50 | |
51 /** | |
52 Returns the required number of function arguments, optional arguments excluded | |
53 */ | |
54 int requiredArgCount(alias fn)() { | |
55 alias ParameterTypeTuple!(typeof(&fn)) P; | |
56 P p; | |
57 static if (P.length == 0) | |
58 return 0; | |
59 | |
60 foreach(i, _; P) | |
61 { | |
62 static if (!__traits(compiles, fn(p[0..$-i-1]))) | |
63 { | |
64 return p.length - i; | |
65 } | |
66 } | |
67 return 0; | |
68 } | |
69 | |
70 /** | |
71 Returns true if is a delegate type. | |
72 */ | |
73 template isDelegate(T) | |
74 { | |
75 enum isDelegate = is(T == delegate); | |
76 } | |
77 | |
78 /** | |
79 Returns true if T is a function pointer type. | |
80 */ | |
81 template isFunctionPointer(T) | |
82 { | |
83 enum isFuntionPointer = is(typeof(*T.init) == function); | |
84 } | |
85 | |
86 /** | |
87 Returns true if entity is a function or function type. | |
88 */ | |
89 template isFunction(alias entity) | |
90 { | |
91 static if (is(entity)) | |
92 alias isFunction!(entity, void) isFunction; | |
93 else | |
94 enum isFunction = is(typeof(entity) == function); | |
95 } | |
96 | |
97 template isFunction(T, Dummy = void) | |
98 { | |
99 enum isFunction = is(T == function); | |
100 } | |
101 | |
102 | |
103 //TODO: hack | |
104 uint isModule(string str) | |
105 { | |
106 return startsWith(str, "module "); | |
107 } | |
108 | |
109 | |
110 template qualifiedCppName(T) | |
111 { | |
112 static if(!isModule(__traits(parent, T).stringof)) | |
113 enum qualifiedCppName = qualifiedCppName!(__traits(parent, T)) ~ "::" ~ T.stringof; | |
114 else | |
115 enum qualifiedCppName = T.stringof; | |
116 } | |
117 | |
118 template qualifiedName(T) | |
119 { | |
120 static if (!isModule(__traits(parent, T).stringof)) | |
121 enum qualifiedName = qualifiedName!(__traits(parent, T)) ~ "." ~ T.stringof; | |
122 else | |
123 enum qualifiedName = T.stringof; | |
124 } | |
125 | |
126 template fullName(T) | |
127 { | |
128 static if (is(T == enum)) | |
129 enum fullName = qualifiedName!T; | |
130 else | |
131 enum fullName = T.stringof; | |
132 } | |
133 | |
134 /** | |
135 */ | |
136 enum AttributeOptions | |
137 { | |
138 /** | |
139 */ | |
140 none, | |
141 | |
142 /** | |
143 Allows multiple equally named attributes to be associated with the symbol. | |
144 */ | |
145 allowMultiple = 0x0000_0001, | |
146 | |
147 /* internal */ inner = 0x0000_0002, | |
148 } | |
149 | |
150 /** | |
151 When mixed in an aggregate, converts a compile-time tuple to | |
152 members of that aggregate. | |
153 */ | |
154 mixin template tupleToMembers(string nameSpace, size_t index, A...) | |
155 { | |
156 static if (index < A.length) | |
157 { | |
158 enum indexStr = to!string(index); | |
159 | |
160 static if (is(typeof( { typeof(A[index]) x; }() ))) | |
161 mixin("typeof(A[" ~ indexStr ~ "]) " ~ nameSpace ~ indexStr ~ " = A[" ~ indexStr ~"];\n"); | |
162 else | |
163 mixin("alias Alias!(A[" ~ indexStr ~ "]) " ~ nameSpace ~ indexStr ~ ";\n"); | |
164 | |
165 mixin tupleToMembers!(nameSpace, index + 1, A); | |
166 } | |
167 } | |
168 | |
169 /** | |
170 When mixed in an aggregate, converts a compile-time tuple of name-value pairs to | |
171 members of that aggregate. | |
172 */ | |
173 mixin template NameValueTupleToFields(A...) | |
174 { | |
175 | |
176 } | |
177 | |
178 version (QtdUnittest) | |
179 { | |
180 unittest | |
181 { | |
182 static int foo() | |
183 { | |
184 return 42; | |
185 } | |
186 | |
187 static struct S | |
188 { | |
189 mixin tupleToMembers!("member", 0, | |
190 int, | |
191 "a", | |
192 22, | |
193 foo); | |
194 } | |
195 | |
196 static assert(is(S.member0 == int)); | |
197 S s; | |
198 assert(s.member1 == "a"); | |
199 assert(s.member2 == 22); | |
200 assert(S.member3() == 42); | |
201 } | |
202 } | |
203 | |
204 private template attributeId(alias symbol, uint index = 0) | |
205 { | |
206 enum attributeId = standardNamespace ~ "_attr_" ~ uniqueId!symbol ~ "_" ~ to!string(index); | |
207 } | |
208 | |
209 /** | |
210 Attributes allow to associate arbitrary compile-time data | |
211 with a declaration and optionaly make that data available at run-time. | |
212 | |
213 ---- | |
214 class A | |
215 { | |
216 } | |
217 mixin Attribute!(C, "someAttribute", "B"); | |
218 ---- | |
219 | |
220 The example above associates the string "B" with class A under the attribute name "someAttribute". | |
221 Multiple attributes with the same name can be associated with a single declaration. | |
222 | |
223 ---- | |
224 class A | |
225 { | |
226 } | |
227 mixin Attribute!(C, "someAttribute", "B"); | |
228 mixin Attribute!(C, "someAttribute", AttributeOptions.allowMultiple, "C"); | |
229 ---- | |
230 | |
231 Attributes can be accessed at compile-time as follows: | |
232 | |
233 ---- | |
234 alias GetAttributes!(C, "someAttribute") attrs; | |
235 ---- | |
236 | |
237 GetAttribute returns a static tuple, which contains "someAttribute" attributes in the form | |
238 of TypeTupleWrapper instances. The wrapped tuples have the following layout: | |
239 | |
240 [0] - attribute name | |
241 [1] - attribute options | |
242 [2..$] - attribute data | |
243 | |
244 ---- | |
245 alias attrs[0] attr0; | |
246 alias attrs[1] attr1; | |
247 | |
248 static assert(attrs0.tuple[0] == "someAttribute" && attrs0.tuple[2] == "B"); | |
249 static assert(attrs1.tuple[0] == "someAttribute" && attrs1.tuple[2] == "C"); | |
250 ---- | |
251 | |
252 Attributes can be inserted inside the body of a declaration. | |
253 ---- | |
254 class A | |
255 { | |
256 mixin Attribute!("someAttribute", "B"); | |
257 } | |
258 ---- | |
259 | |
260 Attributes can be made available at run time by means of the declaration's meta-object | |
261 ---- | |
262 // prints names of all attributes associated with class A | |
263 auto a = new A; | |
264 foreach (attr; a.metaObject.attributes) | |
265 writeln(attr.name); | |
266 ---- | |
267 | |
268 Attributes can be specialized | |
269 | |
270 ---- | |
271 mixin template DbFieldAttribute(alias prop, string columnName) | |
272 { | |
273 mixin Attribute!(prop, "DbFieldAttribute", columnName); | |
274 } | |
275 | |
276 class A | |
277 { | |
278 int value; | |
279 mixin DbFieldAttribute!(value, "Value"); | |
280 | |
281 int anotherValue; | |
282 mixin DbFieldAttribute!(anotherValue, "Value2"); | |
283 } | |
284 | |
285 assert(GetAttributes!(A.value, "DbFieldAttribute")[0].tuple[1] == "Value"); | |
286 assert(GetAttributes!(A.anotherValue, "DbFieldAttribute")[0].tuple[1] == "Value2"); | |
287 ---- | |
288 | |
289 */ | |
290 mixin template Attribute(alias symbol, string attrClass, A...) | |
291 { | |
292 mixin Attribute!(symbol, attrClass, AttributeOptions.none, A); | |
293 } | |
294 | |
295 /// ditto | |
296 mixin template Attribute(alias symbol, string attrClass, AttributeOptions opts, A...) | |
297 { | |
298 mixin AttributeImpl!(symbol, attrClass, opts, 0, A); | |
299 } | |
300 | |
301 | |
302 /// ditto | |
303 // TODO: probably make this an overload of Attribute | |
304 mixin template InnerAttribute(string attrClass, AttributeOptions opts, A...) | |
305 { | |
306 // BUG: needs to be generalized to accept any parent | |
307 mixin Attribute!(typeof(this), attrClass, opts | AttributeOptions.inner, A); | |
308 } | |
309 | |
310 /// ditto | |
311 mixin template InnerAttribute(string attrClass, A...) | |
312 { | |
313 mixin InnerAttribute!(attrClass, AttributeOptions.none, A); | |
314 } | |
315 | |
316 private mixin template AttributeImpl(alias symbol, string attrClass, AttributeOptions opts, size_t index, A...) | |
317 { | |
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 } |