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