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 */
|
348
|
61 struct 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
|
|
195 // ditto
|
|
196 mixin template InnerAttribute(string attrClass, A...)
|
|
197 {
|
|
198 // BUG: needs to be generalized to accept any parent
|
|
199 mixin InnerAttribute!(attrClass, AttributeOptions.none, A);
|
|
200 }
|
|
201
|
|
202 private mixin template AttributeImpl(alias symbol, string attrClass, AttributeOptions opts, size_t index, A...)
|
|
203 {
|
|
204 private enum attrId = attributeId!(symbol, index) ~ (opts & AttributeOptions.inner ? "_inner" : "");
|
|
205
|
|
206 static if (is(typeof(mixin(attrId))))
|
|
207 {
|
|
208 mixin ("alias " ~ attrId ~ " attr;");
|
|
209 static if (!(opts & AttributeOptions.allowMultiple))
|
|
210 {
|
|
211 static assert (attr[0] != attrClass, "Multiple " ~ attrClass ~ " attributes are not allowed for "
|
|
212 ~ __traits(parent, symbol).stringof ~ "." ~ stringOf!symbol);
|
|
213 }
|
|
214
|
|
215 mixin AttributeImpl!(symbol, attrClass, opts, index + 1, A);
|
|
216 }
|
|
217 else
|
|
218 mixin ("alias TypeTuple!(attrClass, A) " ~ attrId ~ ";");
|
|
219 }
|
|
220
|
347
|
221 /**
|
348
|
222 Base class for run time attribute implementations
|
347
|
223 */
|
|
224 abstract class MetaAttribute
|
|
225 {
|
|
226 }
|
|
227
|
|
228 /**
|
348
|
229 A run-time attribute implementation that stores the attribute data in an
|
|
230 array of variants.
|
347
|
231 */
|
348
|
232 final class MetaVariantAttribute : MetaAttribute
|
347
|
233 {
|
348
|
234 Variant[] values;
|
347
|
235
|
348
|
236 private this()
|
347
|
237 {
|
|
238 }
|
|
239
|
348
|
240 static MetaVariantAttribute create(string category, AttributeOptions opts, A...)()
|
347
|
241 {
|
348
|
242 auto ret = new MetaVariantAttribute;
|
|
243 super.construct!(category, opts, A)();
|
|
244 foreach(i, _; A)
|
|
245 {
|
|
246 static if (__traits(compiles, { ret.values ~= Variant(A[i]); }() ))
|
|
247 ret.values ~= Variant(A[i]);
|
|
248 }
|
|
249 return ret;
|
347
|
250 }
|
|
251 }
|
|
252
|
348
|
253 /**
|
|
254 A run-time attribute implementation that stores the attribute data in an
|
|
255 assiciative array of variants.
|
|
256 */
|
|
257 final class MetaVariantDictAttribute : MetaAttribute
|
347
|
258 {
|
348
|
259 Variant[string] values;
|
347
|
260
|
348
|
261 private this()
|
|
262 {
|
|
263 }
|
|
264
|
|
265 static MetaVariantAttribute create(string category, AttributeOptions opts, A...)()
|
347
|
266 {
|
348
|
267 auto ret = new MetaVariantAttribute;
|
|
268 super.construct!(category, opts, A)();
|
|
269 foreach(i, _; A)
|
|
270 {
|
|
271 static if (i % 2 == 0 && __traits(compiles, { ret.values[A[i]] = Variant(A[i + 1]); }() ))
|
|
272 ret.values[A[i]] ~= Variant(A[i + 1]);
|
|
273 }
|
|
274 return ret;
|
347
|
275 }
|
|
276 }
|
|
277
|
|
278
|
348
|
279 /**
|
|
280 A run-time attribute implementation that stores the attribute data in
|
|
281 typed fields named fieldN, where N is the index of the original attribute data element.
|
|
282 */
|
|
283 abstract class MetaTypedAttribute : MetaAttribute
|
|
284 {
|
|
285 private this() {}
|
|
286
|
|
287 static class Impl(A) : typeof(this)
|
|
288 {
|
|
289 private this() {}
|
|
290
|
|
291 mixin tupleToMembers!("field", 0, A);
|
|
292 }
|
|
293
|
|
294 static MetaAttribute create(string category, AttributeOptions opts, A...)()
|
|
295 {
|
|
296 auto ret = new Impl!A;
|
|
297 super.construct(category, opts, A);
|
|
298 return ret;
|
|
299 }
|
|
300 }
|
|
301
|
|
302 /**
|
|
303 A run-time attribute implementation that stores the attribute data in
|
|
304 typed fields by interpreting the original attribute data as name-value pairs.
|
|
305 */
|
|
306 abstract class MetaTypedDictAttribute : MetaAttribute
|
|
307 {
|
|
308 private this() {}
|
|
309
|
|
310 static class Impl(A) : typeof(this)
|
|
311 {
|
|
312 private this() {}
|
|
313
|
|
314 mixin nameValueTupleToMembers!("", A);
|
|
315 }
|
|
316
|
|
317 static MetaAttribute create(string category, AttributeOptions opts, A...)()
|
|
318 {
|
|
319 auto ret = new Impl!A;
|
|
320 super.construct(category, opts, A);
|
|
321 return ret;
|
|
322 }
|
|
323 }
|
|
324
|
|
325 version (QtdUnittest)
|
|
326 {
|
|
327 unittest
|
|
328 {
|
|
329 static void foo() {}
|
|
330
|
|
331 static class C
|
|
332 {
|
|
333 mixin InnerAttribute!("someAttribute", MetaVariantAttribute, "22", foo, 33);
|
|
334 }
|
|
335
|
|
336 auto attr = cast(MetaVariantAttribute) meta!(C).attributes[0];
|
|
337 assert(attr.name == "someAttribute");
|
|
338 assert(qttr.length == 2);
|
|
339 assert(attr.values[0] == "22");
|
|
340 assert(attr.values[1] == 33);
|
|
341 }
|
|
342 }
|
347
|
343
|
345
|
344 private string stringOfFunction(alias symbol)()
|
|
345 {
|
|
346 auto ptrType = typeof(&symbol).stringof;
|
|
347 auto paramList = ParameterTypeTuple!(symbol).stringof;
|
|
348
|
|
349 string result = ReturnType!(symbol).stringof ~ " " ~ __traits(identifier, symbol) ~ paramList;
|
|
350
|
|
351 if (ptrType[$ - 1] != ')')
|
|
352 {
|
|
353 for (size_t i = ptrType.length - 2;; --i)
|
|
354 {
|
|
355 if (ptrType[i] == ')')
|
|
356 {
|
|
357 result ~= ptrType[i + 1..$];
|
|
358 break;
|
|
359 }
|
|
360 }
|
|
361 }
|
|
362
|
|
363 return result;
|
|
364 }
|
|
365
|
|
366 /**
|
|
367 String of any symbol, including functions
|
|
368 */
|
|
369 template stringOf(alias symbol)
|
|
370 {
|
|
371 static if (isFunction!symbol)
|
|
372 enum stringOf = stringOfFunction!symbol;
|
|
373 else
|
|
374 enum stringOf = symbol.stringof;
|
|
375 }
|
|
376
|
|
377 /**
|
|
378 Returns the string uniquely identifying the
|
|
379 symbol in its container.
|
|
380 */
|
|
381 template uniqueName(alias symbol)
|
|
382 {
|
|
383 enum uniqueName = stringOf!symbol;
|
|
384 }
|
|
385
|
|
386 // TODO: works only for simple types. implement
|
|
387 /**
|
|
388 *
|
|
389 */
|
|
390 string uniqueIdImpl(string symbol)
|
|
391 {
|
|
392 char[] r = symbol.dup;
|
|
393 foreach (i, c; symbol)
|
|
394 {
|
|
395 if (c == '(' || c == ')' || c == ' ')
|
|
396 r[i] = '_';
|
|
397 }
|
|
398
|
|
399 return cast(immutable)r;
|
|
400 }
|
|
401
|
|
402 template uniqueId(alias symbol)
|
|
403 {
|
|
404 enum uniqueId = uniqueIdImpl(stringOf!symbol);
|
|
405 }
|
|
406
|
|
407 version (QtdUnittest)
|
|
408 {
|
|
409 unittest
|
|
410 {
|
|
411 static class C
|
|
412 {
|
|
413 void foo() const {};
|
|
414 bool bar(int) { return true; };
|
|
415 int x;
|
|
416 static assert (stringOf!foo == "void foo() const");
|
|
417 static assert (uniqueName!foo == "void foo() const");
|
|
418 static assert (stringOf!bar == "bool bar(int)");
|
|
419 static assert (uniqueName!bar == "bool bar(int)");
|
|
420 static assert (stringOf!x == "x");
|
|
421 static assert (uniqueName!x == "x");
|
|
422 }
|
|
423
|
|
424 static assert (stringOf!(C.foo) == "void foo() const");
|
|
425 static assert (uniqueName!(C.foo) == "void foo() const");
|
|
426 static assert (stringOf!(C.bar) == "bool bar(int)");
|
|
427 static assert (uniqueName!(C.bar) == "bool bar(int)");
|
|
428
|
|
429 static assert (stringOf!(C.x) == "x");
|
|
430 static assert (uniqueName!(C.x) == "x");
|
|
431 }
|
|
432 }
|
|
433
|
|
434 template truePred(A...)
|
|
435 {
|
|
436 enum truePred = true;
|
|
437 }
|
|
438
|
|
439 template attrCategoryPred(string name)
|
|
440 {
|
|
441 template attrCategoryPred(A...)
|
|
442 {
|
|
443 enum attrCategoryPred = A[0] == name;
|
|
444 }
|
|
445 }
|
|
446
|
|
447 /**
|
|
448 Returns a compile-time tuple of attributes that
|
|
449 match pred.
|
|
450 */
|
|
451 template GetAttributes(alias symbol, alias pred = truePred)
|
|
452 {
|
|
453 alias GetAttributesImpl!(symbol, 0, pred).result GetAttributes;
|
|
454 }
|
|
455
|
|
456 /**
|
|
457 Returns a compile-time tuple of attributes
|
|
458 matching the specified attribute category.
|
|
459 */
|
|
460 template GetAttributes(alias symbol, string attrCategory)
|
|
461 {
|
|
462 alias GetAttributes!(symbol, attrCategoryPred!attrCategory) GetAttributes;
|
|
463 }
|
|
464
|
|
465
|
|
466 template GetAttributesImpl(alias symbol, size_t index, alias pred)
|
|
467 {
|
|
468 //pragma(msg, mixin("symbol." ~ attributeId!(symbol, index) ~ "_inner").stringof);
|
|
469 static if (is(typeof(mixin("__traits(parent, symbol)." ~ attributeId!(symbol, index)))))
|
|
470 mixin ("alias lds.meta.compiletime.Alias!(__traits(parent, symbol))." ~ attributeId!(symbol, index) ~ " attr;");
|
|
471 else static if (is(typeof(mixin("symbol." ~ attributeId!(symbol, index) ~ "_inner"))))
|
|
472 mixin ("alias symbol." ~ attributeId!(symbol, index) ~ "_inner attr;");
|
|
473
|
|
474 static if (is(typeof(attr)))
|
|
475 {
|
|
476 alias GetAttributesImpl!(symbol, index + 1, pred).result next;
|
|
477
|
|
478 static if (pred!attr)
|
|
479 alias TypeTuple!(TypeTupleWrapper!attr, next) result;
|
|
480 else
|
|
481 alias next result;
|
|
482 }
|
|
483 else
|
|
484 alias TypeTuple!() result;
|
|
485 }
|
|
486
|
|
487 version (QtdUnittest)
|
|
488 {
|
|
489 mixin template MyAttribute(alias symbol, A...)
|
|
490 {
|
|
491 mixin Attribute!(symbol, "MyAttribute", AttributeOptions.allowMultiple, A);
|
|
492 }
|
|
493
|
|
494 mixin template ClassInfo(string name, alias value)
|
|
495 {
|
|
496 mixin InnerAttribute!("ClassInfo", AttributeOptions.allowMultiple, name, value);
|
|
497 }
|
|
498
|
|
499 unittest
|
|
500 {
|
|
501 static class C
|
|
502 {
|
|
503 // inner C attributes
|
|
504 mixin InnerAttribute!("Inner", 33); // generic
|
|
505 mixin ClassInfo!("version", 123);
|
|
506 mixin ClassInfo!("author", "James Bond");
|
|
507
|
|
508
|
|
509 void foo() {};
|
|
510 // foo attributes
|
|
511 mixin Attribute!(foo, "SomeAttribute", 42);
|
|
512 mixin MyAttribute!(foo, 1, 2);
|
|
513 mixin MyAttribute!(foo, 3, 4);
|
|
514
|
|
515 alias GetAttributes!(typeof(this), "Inner") innerAttrs;
|
|
516 static assert(innerAttrs[0].tuple[0] == "Inner");
|
|
517 }
|
|
518 // outer C attribute
|
|
519 mixin MyAttribute!(C, 24);
|
|
520
|
|
521 alias GetAttributes!(C, "Inner") innerAttrs;
|
|
522 static assert(innerAttrs[0].tuple[0] == "Inner" && innerAttrs[0].tuple[1] == 33);
|
|
523
|
|
524 alias GetAttributes!(C, "ClassInfo") ciAttrs;
|
|
525 static assert(ciAttrs[0].tuple[1] == "version" && ciAttrs[0].tuple[2] == 123);
|
|
526
|
|
527 alias GetAttributes!(C.foo, "SomeAttribute") someAttr;
|
|
528 static assert(someAttr.length == 1);
|
|
529 static assert(someAttr[0].tuple[0] == "SomeAttribute");
|
|
530
|
|
531 alias GetAttributes!(C.foo, "MyAttribute") myAttrs;
|
|
532
|
|
533 //COMPILER BUG: cannot 'alias myAttrs[0].tuple myAttrs_0';
|
|
534 static assert(myAttrs[0].tuple[0] == "MyAttribute");
|
|
535 static assert(myAttrs[0].tuple[1] == 1 && myAttrs[0].tuple[2] == 2);
|
|
536
|
|
537 static assert(myAttrs[1].tuple[0] == "MyAttribute");
|
|
538 static assert(myAttrs[1].tuple[1] == 3 && myAttrs[1].tuple[2] == 4);
|
|
539
|
|
540 /+ BUG: Fails: local declarations cannot be accessed as parent.localDecl
|
|
541 alias GetAttributes!(C, "MyAttribute") myAttrs2;
|
|
542 static assert(myAttrs2[0].tuple[0] == "MyAttribute");
|
|
543 static assert(myAttrs2[0].tuple[1] == 24);
|
|
544 +/
|
|
545 }
|
|
546 } |