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 }