changeset 345:719604a71da0

added attribute exercise
author Max Samukha <maxter@spambox.com>
date Fri, 14 May 2010 12:38:00 +0300
parents 96a75b1e5b26
children 2691dd58d7e1
files d2/qtd/Attribute.d
diffstat 1 files changed, 361 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /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