view d2/qtd/Attribute.d @ 346:2691dd58d7e1

fixed QSlitter.getRange
author Max Samukha <maxter@spambox.com>
date Fri, 14 May 2010 13:15:24 +0300
parents 719604a71da0
children 970332a88b72
line wrap: on
line source

/*********************************************************
    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,

    /* internal */ inner         = 0x0000_0002
}

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);
        +/
    }
}