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,
|
|
12 std.typetuple;
|
|
13
|
|
14 enum standardNamespace = "qtd";
|
|
15
|
|
16 /**
|
|
17 */
|
|
18 enum AttributeOptions
|
|
19 {
|
|
20 /**
|
|
21 */
|
|
22 none,
|
|
23
|
|
24 /**
|
|
25 Allows multiple attributes of the same category to be associated with the symbol.
|
|
26 */
|
346
|
27 allowMultiple = 0x0000_0001,
|
|
28
|
|
29 /* internal */ inner = 0x0000_0002
|
345
|
30 }
|
|
31
|
|
32 private template attributeId(alias symbol, uint index = 0)
|
|
33 {
|
|
34 enum attributeId = standardNamespace ~ "_attr_" ~ uniqueId!symbol ~ "_" ~ to!string(index);
|
|
35 }
|
|
36
|
|
37 /**
|
|
38 Attributes allow to associate arbitrary compile-time data
|
|
39 with a declaration and optionaly make that data available at run-time.
|
|
40
|
|
41 ----
|
|
42 class A
|
|
43 {
|
|
44 }
|
|
45 mixin Attribute!(C, "someAttribute", "B");
|
|
46 ----
|
|
47
|
|
48 The example above associates the string "B" with class A under the attribute category "someAttribute".
|
|
49 Multiple data can be associated with an attribute category for a single declaration:
|
|
50
|
|
51 ----
|
|
52 class A
|
|
53 {
|
|
54 }
|
|
55 mixin Attribute!(C, "someAttribute", "B");
|
|
56 mixin Attribute!(C, "someAttribute", AttributeOptions.allowMultiple, "C");
|
|
57 ----
|
|
58
|
|
59 Attributes of an attribute category can be accessed at compile-time:
|
|
60
|
|
61 ----
|
|
62 alias GetAttributes!(C, "someAttribute") attrs;
|
|
63 ----
|
|
64
|
|
65 GetAttribute returns a compile time tuple, which contains "someAttribute" category attributes in the form
|
|
66 of TypeTupleWrapper instances. The first element of the tuple wrapped in the TypeTupleWrapper
|
|
67 contains the attribute category name, other elements contain the attrubute data.
|
|
68
|
|
69 ----
|
|
70 alias attrs[0] attr0;
|
|
71 alias attrs[1] attr1;
|
|
72
|
|
73 static assert(attrs0.tuple[0] == "someAttribute" && attrs0.tuple[1] == "B");
|
|
74 static assert(attrs1.tuple[0] == "someAttribute" && attrs1.tuple[1] == "C");
|
|
75 ----
|
|
76
|
|
77 Attributes can be inserted inside the body of a declaration.
|
|
78 ----
|
|
79 class A
|
|
80 {
|
|
81 mixin Attribute!("someAttribute", "B");
|
|
82 }
|
|
83 ----
|
|
84
|
|
85 Attributes can be made available at run time by means of the declaration's meta-object
|
|
86 ----
|
|
87 // prints category names of all attributes associated with class A
|
|
88 auto a = new A;
|
|
89 foreach (attr; a.metaObject.attributes)
|
|
90 writeln(attr.name);
|
|
91 ----
|
|
92
|
|
93 Attributes can be specialized
|
|
94
|
|
95 ----
|
|
96 mixin template DbFieldAttribute(alias prop, string columnName)
|
|
97 {
|
|
98 mixin Attribute!(prop, "DbFieldAttribute", columnName);
|
|
99 }
|
|
100
|
|
101 class A
|
|
102 {
|
|
103 int value;
|
|
104 mixin DbFieldAttribute!(value, "Value");
|
|
105
|
|
106 int anotherValue;
|
|
107 mixin DbFieldAttribute!(anotherValue, "Value2");
|
|
108 }
|
|
109
|
|
110 assert(GetAttributes!(A.value, "DbFieldAttribute")[0].tuple[1] == "Value");
|
|
111 assert(GetAttributes!(A.anotherValue, "DbFieldAttribute")[0].tuple[1] == "Value2");
|
|
112 ----
|
|
113
|
|
114 */
|
|
115 mixin template Attribute(alias symbol, string attrClass, A...)
|
|
116 {
|
|
117 mixin Attribute!(symbol, attrClass, AttributeOptions.none, A);
|
|
118 }
|
|
119
|
|
120 /// ditto
|
|
121 mixin template Attribute(alias symbol, string attrClass, AttributeOptions opts, A...)
|
|
122 {
|
|
123 mixin AttributeImpl!(symbol, attrClass, opts, 0, A);
|
|
124 }
|
|
125
|
|
126
|
|
127 /// ditto
|
|
128 // TODO: probably make this an overload of Attribute
|
|
129 mixin template InnerAttribute(string attrClass, AttributeOptions opts, A...)
|
|
130 {
|
|
131 // BUG: needs to be generalized to accept any parent
|
|
132 mixin Attribute!(typeof(this), attrClass, opts | AttributeOptions.inner, A);
|
|
133 }
|
|
134
|
|
135 // ditto
|
|
136 mixin template InnerAttribute(string attrClass, A...)
|
|
137 {
|
|
138 // BUG: needs to be generalized to accept any parent
|
|
139 mixin InnerAttribute!(attrClass, AttributeOptions.none, A);
|
|
140 }
|
|
141
|
|
142 private mixin template AttributeImpl(alias symbol, string attrClass, AttributeOptions opts, size_t index, A...)
|
|
143 {
|
|
144 private enum attrId = attributeId!(symbol, index) ~ (opts & AttributeOptions.inner ? "_inner" : "");
|
|
145
|
|
146 static if (is(typeof(mixin(attrId))))
|
|
147 {
|
|
148 mixin ("alias " ~ attrId ~ " attr;");
|
|
149 static if (!(opts & AttributeOptions.allowMultiple))
|
|
150 {
|
|
151 static assert (attr[0] != attrClass, "Multiple " ~ attrClass ~ " attributes are not allowed for "
|
|
152 ~ __traits(parent, symbol).stringof ~ "." ~ stringOf!symbol);
|
|
153 }
|
|
154
|
|
155 mixin AttributeImpl!(symbol, attrClass, opts, index + 1, A);
|
|
156 }
|
|
157 else
|
|
158 mixin ("alias TypeTuple!(attrClass, A) " ~ attrId ~ ";");
|
|
159 }
|
|
160
|
|
161 private string stringOfFunction(alias symbol)()
|
|
162 {
|
|
163 auto ptrType = typeof(&symbol).stringof;
|
|
164 auto paramList = ParameterTypeTuple!(symbol).stringof;
|
|
165
|
|
166 string result = ReturnType!(symbol).stringof ~ " " ~ __traits(identifier, symbol) ~ paramList;
|
|
167
|
|
168 if (ptrType[$ - 1] != ')')
|
|
169 {
|
|
170 for (size_t i = ptrType.length - 2;; --i)
|
|
171 {
|
|
172 if (ptrType[i] == ')')
|
|
173 {
|
|
174 result ~= ptrType[i + 1..$];
|
|
175 break;
|
|
176 }
|
|
177 }
|
|
178 }
|
|
179
|
|
180 return result;
|
|
181 }
|
|
182
|
|
183 /**
|
|
184 String of any symbol, including functions
|
|
185 */
|
|
186 template stringOf(alias symbol)
|
|
187 {
|
|
188 static if (isFunction!symbol)
|
|
189 enum stringOf = stringOfFunction!symbol;
|
|
190 else
|
|
191 enum stringOf = symbol.stringof;
|
|
192 }
|
|
193
|
|
194 /**
|
|
195 Returns the string uniquely identifying the
|
|
196 symbol in its container.
|
|
197 */
|
|
198 template uniqueName(alias symbol)
|
|
199 {
|
|
200 enum uniqueName = stringOf!symbol;
|
|
201 }
|
|
202
|
|
203 // TODO: works only for simple types. implement
|
|
204 /**
|
|
205 *
|
|
206 */
|
|
207 string uniqueIdImpl(string symbol)
|
|
208 {
|
|
209 char[] r = symbol.dup;
|
|
210 foreach (i, c; symbol)
|
|
211 {
|
|
212 if (c == '(' || c == ')' || c == ' ')
|
|
213 r[i] = '_';
|
|
214 }
|
|
215
|
|
216 return cast(immutable)r;
|
|
217 }
|
|
218
|
|
219 template uniqueId(alias symbol)
|
|
220 {
|
|
221 enum uniqueId = uniqueIdImpl(stringOf!symbol);
|
|
222 }
|
|
223
|
|
224 version (QtdUnittest)
|
|
225 {
|
|
226 unittest
|
|
227 {
|
|
228 static class C
|
|
229 {
|
|
230 void foo() const {};
|
|
231 bool bar(int) { return true; };
|
|
232 int x;
|
|
233 static assert (stringOf!foo == "void foo() const");
|
|
234 static assert (uniqueName!foo == "void foo() const");
|
|
235 static assert (stringOf!bar == "bool bar(int)");
|
|
236 static assert (uniqueName!bar == "bool bar(int)");
|
|
237 static assert (stringOf!x == "x");
|
|
238 static assert (uniqueName!x == "x");
|
|
239 }
|
|
240
|
|
241 static assert (stringOf!(C.foo) == "void foo() const");
|
|
242 static assert (uniqueName!(C.foo) == "void foo() const");
|
|
243 static assert (stringOf!(C.bar) == "bool bar(int)");
|
|
244 static assert (uniqueName!(C.bar) == "bool bar(int)");
|
|
245
|
|
246 static assert (stringOf!(C.x) == "x");
|
|
247 static assert (uniqueName!(C.x) == "x");
|
|
248 }
|
|
249 }
|
|
250
|
|
251 template truePred(A...)
|
|
252 {
|
|
253 enum truePred = true;
|
|
254 }
|
|
255
|
|
256 template attrCategoryPred(string name)
|
|
257 {
|
|
258 template attrCategoryPred(A...)
|
|
259 {
|
|
260 enum attrCategoryPred = A[0] == name;
|
|
261 }
|
|
262 }
|
|
263
|
|
264 /**
|
|
265 Returns a compile-time tuple of attributes that
|
|
266 match pred.
|
|
267 */
|
|
268 template GetAttributes(alias symbol, alias pred = truePred)
|
|
269 {
|
|
270 alias GetAttributesImpl!(symbol, 0, pred).result GetAttributes;
|
|
271 }
|
|
272
|
|
273 /**
|
|
274 Returns a compile-time tuple of attributes
|
|
275 matching the specified attribute category.
|
|
276 */
|
|
277 template GetAttributes(alias symbol, string attrCategory)
|
|
278 {
|
|
279 alias GetAttributes!(symbol, attrCategoryPred!attrCategory) GetAttributes;
|
|
280 }
|
|
281
|
|
282
|
|
283 template GetAttributesImpl(alias symbol, size_t index, alias pred)
|
|
284 {
|
|
285 //pragma(msg, mixin("symbol." ~ attributeId!(symbol, index) ~ "_inner").stringof);
|
|
286 static if (is(typeof(mixin("__traits(parent, symbol)." ~ attributeId!(symbol, index)))))
|
|
287 mixin ("alias lds.meta.compiletime.Alias!(__traits(parent, symbol))." ~ attributeId!(symbol, index) ~ " attr;");
|
|
288 else static if (is(typeof(mixin("symbol." ~ attributeId!(symbol, index) ~ "_inner"))))
|
|
289 mixin ("alias symbol." ~ attributeId!(symbol, index) ~ "_inner attr;");
|
|
290
|
|
291 static if (is(typeof(attr)))
|
|
292 {
|
|
293 alias GetAttributesImpl!(symbol, index + 1, pred).result next;
|
|
294
|
|
295 static if (pred!attr)
|
|
296 alias TypeTuple!(TypeTupleWrapper!attr, next) result;
|
|
297 else
|
|
298 alias next result;
|
|
299 }
|
|
300 else
|
|
301 alias TypeTuple!() result;
|
|
302 }
|
|
303
|
|
304 version (QtdUnittest)
|
|
305 {
|
|
306 mixin template MyAttribute(alias symbol, A...)
|
|
307 {
|
|
308 mixin Attribute!(symbol, "MyAttribute", AttributeOptions.allowMultiple, A);
|
|
309 }
|
|
310
|
|
311 mixin template ClassInfo(string name, alias value)
|
|
312 {
|
|
313 mixin InnerAttribute!("ClassInfo", AttributeOptions.allowMultiple, name, value);
|
|
314 }
|
|
315
|
|
316 unittest
|
|
317 {
|
|
318 static class C
|
|
319 {
|
|
320 // inner C attributes
|
|
321 mixin InnerAttribute!("Inner", 33); // generic
|
|
322 mixin ClassInfo!("version", 123);
|
|
323 mixin ClassInfo!("author", "James Bond");
|
|
324
|
|
325
|
|
326 void foo() {};
|
|
327 // foo attributes
|
|
328 mixin Attribute!(foo, "SomeAttribute", 42);
|
|
329 mixin MyAttribute!(foo, 1, 2);
|
|
330 mixin MyAttribute!(foo, 3, 4);
|
|
331
|
|
332 alias GetAttributes!(typeof(this), "Inner") innerAttrs;
|
|
333 static assert(innerAttrs[0].tuple[0] == "Inner");
|
|
334 }
|
|
335 // outer C attribute
|
|
336 mixin MyAttribute!(C, 24);
|
|
337
|
|
338 alias GetAttributes!(C, "Inner") innerAttrs;
|
|
339 static assert(innerAttrs[0].tuple[0] == "Inner" && innerAttrs[0].tuple[1] == 33);
|
|
340
|
|
341 alias GetAttributes!(C, "ClassInfo") ciAttrs;
|
|
342 static assert(ciAttrs[0].tuple[1] == "version" && ciAttrs[0].tuple[2] == 123);
|
|
343
|
|
344 alias GetAttributes!(C.foo, "SomeAttribute") someAttr;
|
|
345 static assert(someAttr.length == 1);
|
|
346 static assert(someAttr[0].tuple[0] == "SomeAttribute");
|
|
347
|
|
348 alias GetAttributes!(C.foo, "MyAttribute") myAttrs;
|
|
349
|
|
350 //COMPILER BUG: cannot 'alias myAttrs[0].tuple myAttrs_0';
|
|
351 static assert(myAttrs[0].tuple[0] == "MyAttribute");
|
|
352 static assert(myAttrs[0].tuple[1] == 1 && myAttrs[0].tuple[2] == 2);
|
|
353
|
|
354 static assert(myAttrs[1].tuple[0] == "MyAttribute");
|
|
355 static assert(myAttrs[1].tuple[1] == 3 && myAttrs[1].tuple[2] == 4);
|
|
356
|
|
357 /+ BUG: Fails: local declarations cannot be accessed as parent.localDecl
|
|
358 alias GetAttributes!(C, "MyAttribute") myAttrs2;
|
|
359 static assert(myAttrs2[0].tuple[0] == "MyAttribute");
|
|
360 static assert(myAttrs2[0].tuple[1] == 24);
|
|
361 +/
|
|
362 }
|
|
363 } |