# HG changeset patch # User Max Samukha # Date 1273829880 -10800 # Node ID 719604a71da0828a192cf7e0a73b4b81c8c1293c # Parent 96a75b1e5b2694be7531e990c81cdf9103e4a185 added attribute exercise diff -r 96a75b1e5b26 -r 719604a71da0 d2/qtd/Attribute.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/d2/qtd/Attribute.d Fri May 14 12:38:00 2010 +0300 @@ -0,0 +1,361 @@ +/********************************************************* + Copyright: QtD Team, 2010 + Authors: Max Samukha + License: Boost Software License 1.0 +*********************************************************/ +module qtd.Attribute; + +import + lds.meta.compiletime, + std.traits, + std.conv, + std.typetuple; + +enum standardNamespace = "qtd"; + +/** + */ +enum AttributeOptions +{ + /** + */ + none, + + /** + Allows multiple attributes of the same category to be associated with the symbol. + */ + allowMultiple = 0x0000_0001, +} + +private template attributeId(alias symbol, uint index = 0) +{ + enum attributeId = standardNamespace ~ "_attr_" ~ uniqueId!symbol ~ "_" ~ to!string(index); +} + +/** + Attributes allow to associate arbitrary compile-time data + with a declaration and optionaly make that data available at run-time. + +---- +class A +{ +} +mixin Attribute!(C, "someAttribute", "B"); +---- + + The example above associates the string "B" with class A under the attribute category "someAttribute". + Multiple data can be associated with an attribute category for a single declaration: + +---- +class A +{ +} +mixin Attribute!(C, "someAttribute", "B"); +mixin Attribute!(C, "someAttribute", AttributeOptions.allowMultiple, "C"); +---- + + Attributes of an attribute category can be accessed at compile-time: + +---- +alias GetAttributes!(C, "someAttribute") attrs; +---- + + GetAttribute returns a compile time tuple, which contains "someAttribute" category attributes in the form + of TypeTupleWrapper instances. The first element of the tuple wrapped in the TypeTupleWrapper + contains the attribute category name, other elements contain the attrubute data. + +---- +alias attrs[0] attr0; +alias attrs[1] attr1; + +static assert(attrs0.tuple[0] == "someAttribute" && attrs0.tuple[1] == "B"); +static assert(attrs1.tuple[0] == "someAttribute" && attrs1.tuple[1] == "C"); +---- + + Attributes can be inserted inside the body of a declaration. +---- + class A + { + mixin Attribute!("someAttribute", "B"); + } +---- + + Attributes can be made available at run time by means of the declaration's meta-object +---- + // prints category names of all attributes associated with class A + auto a = new A; + foreach (attr; a.metaObject.attributes) + writeln(attr.name); +---- + + Attributes can be specialized + +---- +mixin template DbFieldAttribute(alias prop, string columnName) +{ + mixin Attribute!(prop, "DbFieldAttribute", columnName); +} + +class A +{ + int value; + mixin DbFieldAttribute!(value, "Value"); + + int anotherValue; + mixin DbFieldAttribute!(anotherValue, "Value2"); +} + +assert(GetAttributes!(A.value, "DbFieldAttribute")[0].tuple[1] == "Value"); +assert(GetAttributes!(A.anotherValue, "DbFieldAttribute")[0].tuple[1] == "Value2"); +---- + +*/ +mixin template Attribute(alias symbol, string attrClass, A...) +{ + mixin Attribute!(symbol, attrClass, AttributeOptions.none, A); +} + +/// ditto +mixin template Attribute(alias symbol, string attrClass, AttributeOptions opts, A...) +{ + mixin AttributeImpl!(symbol, attrClass, opts, 0, A); +} + + +/// ditto +// TODO: probably make this an overload of Attribute +mixin template InnerAttribute(string attrClass, AttributeOptions opts, A...) +{ + // BUG: needs to be generalized to accept any parent + mixin Attribute!(typeof(this), attrClass, opts | AttributeOptions.inner, A); +} + +// ditto +mixin template InnerAttribute(string attrClass, A...) +{ + // BUG: needs to be generalized to accept any parent + mixin InnerAttribute!(attrClass, AttributeOptions.none, A); +} + +private mixin template AttributeImpl(alias symbol, string attrClass, AttributeOptions opts, size_t index, A...) +{ + private enum attrId = attributeId!(symbol, index) ~ (opts & AttributeOptions.inner ? "_inner" : ""); + + static if (is(typeof(mixin(attrId)))) + { + mixin ("alias " ~ attrId ~ " attr;"); + static if (!(opts & AttributeOptions.allowMultiple)) + { + static assert (attr[0] != attrClass, "Multiple " ~ attrClass ~ " attributes are not allowed for " + ~ __traits(parent, symbol).stringof ~ "." ~ stringOf!symbol); + } + + mixin AttributeImpl!(symbol, attrClass, opts, index + 1, A); + } + else + mixin ("alias TypeTuple!(attrClass, A) " ~ attrId ~ ";"); +} + +private string stringOfFunction(alias symbol)() +{ + auto ptrType = typeof(&symbol).stringof; + auto paramList = ParameterTypeTuple!(symbol).stringof; + + string result = ReturnType!(symbol).stringof ~ " " ~ __traits(identifier, symbol) ~ paramList; + + if (ptrType[$ - 1] != ')') + { + for (size_t i = ptrType.length - 2;; --i) + { + if (ptrType[i] == ')') + { + result ~= ptrType[i + 1..$]; + break; + } + } + } + + return result; +} + +/** + String of any symbol, including functions + */ +template stringOf(alias symbol) +{ + static if (isFunction!symbol) + enum stringOf = stringOfFunction!symbol; + else + enum stringOf = symbol.stringof; +} + +/** + Returns the string uniquely identifying the + symbol in its container. + */ +template uniqueName(alias symbol) +{ + enum uniqueName = stringOf!symbol; +} + +// TODO: works only for simple types. implement +/** + * + */ +string uniqueIdImpl(string symbol) +{ + char[] r = symbol.dup; + foreach (i, c; symbol) + { + if (c == '(' || c == ')' || c == ' ') + r[i] = '_'; + } + + return cast(immutable)r; +} + +template uniqueId(alias symbol) +{ + enum uniqueId = uniqueIdImpl(stringOf!symbol); +} + +version (QtdUnittest) +{ + unittest + { + static class C + { + void foo() const {}; + bool bar(int) { return true; }; + int x; + static assert (stringOf!foo == "void foo() const"); + static assert (uniqueName!foo == "void foo() const"); + static assert (stringOf!bar == "bool bar(int)"); + static assert (uniqueName!bar == "bool bar(int)"); + static assert (stringOf!x == "x"); + static assert (uniqueName!x == "x"); + } + + static assert (stringOf!(C.foo) == "void foo() const"); + static assert (uniqueName!(C.foo) == "void foo() const"); + static assert (stringOf!(C.bar) == "bool bar(int)"); + static assert (uniqueName!(C.bar) == "bool bar(int)"); + + static assert (stringOf!(C.x) == "x"); + static assert (uniqueName!(C.x) == "x"); + } +} + +template truePred(A...) +{ + enum truePred = true; +} + +template attrCategoryPred(string name) +{ + template attrCategoryPred(A...) + { + enum attrCategoryPred = A[0] == name; + } +} + +/** + Returns a compile-time tuple of attributes that + match pred. + */ +template GetAttributes(alias symbol, alias pred = truePred) +{ + alias GetAttributesImpl!(symbol, 0, pred).result GetAttributes; +} + +/** + Returns a compile-time tuple of attributes + matching the specified attribute category. + */ +template GetAttributes(alias symbol, string attrCategory) +{ + alias GetAttributes!(symbol, attrCategoryPred!attrCategory) GetAttributes; +} + + +template GetAttributesImpl(alias symbol, size_t index, alias pred) +{ + //pragma(msg, mixin("symbol." ~ attributeId!(symbol, index) ~ "_inner").stringof); + static if (is(typeof(mixin("__traits(parent, symbol)." ~ attributeId!(symbol, index))))) + mixin ("alias lds.meta.compiletime.Alias!(__traits(parent, symbol))." ~ attributeId!(symbol, index) ~ " attr;"); + else static if (is(typeof(mixin("symbol." ~ attributeId!(symbol, index) ~ "_inner")))) + mixin ("alias symbol." ~ attributeId!(symbol, index) ~ "_inner attr;"); + + static if (is(typeof(attr))) + { + alias GetAttributesImpl!(symbol, index + 1, pred).result next; + + static if (pred!attr) + alias TypeTuple!(TypeTupleWrapper!attr, next) result; + else + alias next result; + } + else + alias TypeTuple!() result; +} + +version (QtdUnittest) +{ + mixin template MyAttribute(alias symbol, A...) + { + mixin Attribute!(symbol, "MyAttribute", AttributeOptions.allowMultiple, A); + } + + mixin template ClassInfo(string name, alias value) + { + mixin InnerAttribute!("ClassInfo", AttributeOptions.allowMultiple, name, value); + } + + unittest + { + static class C + { + // inner C attributes + mixin InnerAttribute!("Inner", 33); // generic + mixin ClassInfo!("version", 123); + mixin ClassInfo!("author", "James Bond"); + + + void foo() {}; + // foo attributes + mixin Attribute!(foo, "SomeAttribute", 42); + mixin MyAttribute!(foo, 1, 2); + mixin MyAttribute!(foo, 3, 4); + + alias GetAttributes!(typeof(this), "Inner") innerAttrs; + static assert(innerAttrs[0].tuple[0] == "Inner"); + } + // outer C attribute + mixin MyAttribute!(C, 24); + + alias GetAttributes!(C, "Inner") innerAttrs; + static assert(innerAttrs[0].tuple[0] == "Inner" && innerAttrs[0].tuple[1] == 33); + + alias GetAttributes!(C, "ClassInfo") ciAttrs; + static assert(ciAttrs[0].tuple[1] == "version" && ciAttrs[0].tuple[2] == 123); + + alias GetAttributes!(C.foo, "SomeAttribute") someAttr; + static assert(someAttr.length == 1); + static assert(someAttr[0].tuple[0] == "SomeAttribute"); + + alias GetAttributes!(C.foo, "MyAttribute") myAttrs; + + //COMPILER BUG: cannot 'alias myAttrs[0].tuple myAttrs_0'; + static assert(myAttrs[0].tuple[0] == "MyAttribute"); + static assert(myAttrs[0].tuple[1] == 1 && myAttrs[0].tuple[2] == 2); + + static assert(myAttrs[1].tuple[0] == "MyAttribute"); + static assert(myAttrs[1].tuple[1] == 3 && myAttrs[1].tuple[2] == 4); + + /+ BUG: Fails: local declarations cannot be accessed as parent.localDecl + alias GetAttributes!(C, "MyAttribute") myAttrs2; + static assert(myAttrs2[0].tuple[0] == "MyAttribute"); + static assert(myAttrs2[0].tuple[1] == 24); + +/ + } +} \ No newline at end of file