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