changeset 351:59d847a814e3

added meta subdir
author Max Samukha <maxter@spambox.com>
date Thu, 20 May 2010 15:54:06 +0300
parents 31520b2c0b3c
children 1a5569684e30
files d2/qtd/meta/Compiletime.d d2/qtd/meta/Runtime.d
diffstat 2 files changed, 849 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/d2/qtd/meta/Compiletime.d	Thu May 20 15:54:06 2010 +0300
@@ -0,0 +1,557 @@
+/**************************************************************************
+    Copyright: QtD Team, 2010
+    Authors: Max Samukha, Eldar Insafutdinov
+    License: Boost Software License 1.0
+**************************************************************************/
+module qtd.meta.Compiletime;
+
+import
+    std.traits,
+    std.conv,
+    std.variant,
+    std.typetuple;
+
+import std.string : startsWith;
+
+/*
+uint startsWith(string s, string pattern)
+{
+    if (pattern.length <= s.length && s[0..pattern.length] == pattern)
+        return pattern.length;
+
+    return 0;
+}
+*/
+
+
+enum standardNamespace = "qtd";
+
+template Alias(A...) if (A.length == 1)
+{
+    static if (__traits(compiles, { alias A[0] x; }))
+        alias A[0] Alias;
+    else
+        enum Alias = A[0];
+}
+
+/**
+    Encapsulates a static tuple. Useful for preventing tuples from
+    flattening when they are passed to a variadic template.
+*/
+template TypeTupleWrapper(A...)
+{
+    alias A tuple;
+}
+
+// returns the type of a template parameter if there is one
+template templateParam(U : V!(U), alias V)
+{
+    alias U templateParam;
+}
+
+
+/**
+    Returns a tuple with T repeated count times.
+ */
+// TODO: generalize to accept any symbol
+template Repeat(T, size_t count)
+{
+    static if (count)
+        alias TypeTuple!(count, Repeat!(T, count - 1)) Repeat;
+    else
+        alias TypeTuple!() Repeat;
+}
+
+/**
+    Returns the required number of function arguments, optional arguments excluded
+ */
+int requiredArgCount(alias fn)() {
+    alias ParameterTypeTuple!(typeof(&fn)) P;
+    P p;
+    static if (P.length == 0)
+        return 0;
+
+    foreach(i, _; P)
+    {
+        static if (!__traits(compiles, fn(p[0..$-i-1])))
+        {
+            return p.length - i;
+        }
+    }
+    return 0;
+}
+
+/**
+    Returns true if is a delegate type.
+ */
+template isDelegate(T)
+{
+    enum isDelegate = is(T == delegate);
+}
+
+/**
+    Returns true if T is a function pointer type.
+ */
+template isFunctionPointer(T)
+{
+    enum isFuntionPointer = is(typeof(*T.init) == function);
+}
+
+/**
+    Returns true if entity is a function or function type.
+*/
+template isFunction(alias entity)
+{
+    static if (is(entity))
+        alias isFunction!(entity, void) isFunction;
+    else
+        enum isFunction = is(typeof(entity) == function);
+}
+
+template isFunction(T, Dummy = void)
+{
+    enum isFunction = is(T == function);
+}
+
+
+//TODO: hack
+uint isModule(string str)
+{
+    return startsWith(str, "module ");
+}
+
+
+template qualifiedCppName(T)
+{
+    static if(!isModule(__traits(parent, T).stringof))
+        enum qualifiedCppName = qualifiedCppName!(__traits(parent, T)) ~ "::" ~ T.stringof;
+    else
+        enum qualifiedCppName = T.stringof;
+}
+
+template qualifiedName(T)
+{
+    static if (!isModule(__traits(parent, T).stringof))
+        enum qualifiedName = qualifiedName!(__traits(parent, T)) ~ "." ~ T.stringof;
+    else
+        enum qualifiedName = T.stringof;
+}
+
+template fullName(T)
+{
+    static if (is(T == enum))
+        enum fullName = qualifiedName!T;
+    else
+        enum fullName = T.stringof;
+}
+
+/**
+ */
+enum AttributeOptions
+{
+    /**
+     */
+    none,
+
+    /**
+        Allows multiple equally named attributes to be associated with the symbol.
+     */
+    allowMultiple                = 0x0000_0001,
+
+    /* internal */ inner         = 0x0000_0002,
+}
+
+/**
+    When mixed in an aggregate, converts a compile-time tuple to
+    members of that aggregate.
+ */
+mixin template tupleToMembers(string nameSpace, size_t index, A...)
+{
+    static if (index < A.length)
+    {
+        enum indexStr = to!string(index);
+
+        static if (is(typeof( { typeof(A[index]) x; }() )))
+            mixin("typeof(A[" ~ indexStr ~ "]) " ~ nameSpace ~ indexStr ~ " = A[" ~ indexStr ~"];\n");
+        else
+            mixin("alias Alias!(A[" ~ indexStr ~ "]) " ~ nameSpace ~ indexStr ~ ";\n");
+
+        mixin tupleToMembers!(nameSpace, index + 1, A);
+    }
+}
+
+/**
+    When mixed in an aggregate, converts a compile-time tuple of name-value pairs to
+    members of that aggregate.
+ */
+mixin template NameValueTupleToFields(A...)
+{
+
+}
+
+version (QtdUnittest)
+{
+    unittest
+    {
+        static int foo()
+        {
+            return 42;
+        }
+
+        static struct S
+        {
+            mixin tupleToMembers!("member", 0,
+                int,
+                "a",
+                22,
+                foo);
+        }
+
+        static assert(is(S.member0 == int));
+        S s;
+        assert(s.member1 == "a");
+        assert(s.member2 == 22);
+        assert(S.member3() == 42);
+    }
+}
+
+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 name "someAttribute".
+    Multiple attributes with the same name can be associated with a single declaration.
+
+----
+class A
+{
+}
+mixin Attribute!(C, "someAttribute", "B");
+mixin Attribute!(C, "someAttribute", AttributeOptions.allowMultiple, "C");
+----
+
+    Attributes can be accessed at compile-time as follows:
+
+----
+alias GetAttributes!(C, "someAttribute") attrs;
+----
+
+    GetAttribute returns a static tuple, which contains "someAttribute" attributes in the form
+    of TypeTupleWrapper instances. The wrapped tuples have the following layout:
+
+    [0] - attribute name
+    [1] - attribute options
+    [2..$] - attribute data
+
+----
+alias attrs[0] attr0;
+alias attrs[1] attr1;
+
+static assert(attrs0.tuple[0] == "someAttribute" && attrs0.tuple[2] == "B");
+static assert(attrs1.tuple[0] == "someAttribute" && attrs1.tuple[2] == "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 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...)
+{
+    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, opts, 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;
+}
+
+/**
+    Returns a valid D identifier uniquely
+    identifying symbol in its container.
+ */
+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");
+    }
+}
+
+/**
+    Predicate that always evaluates to true regardless of arguments.
+ */
+template truePred(A...)
+{
+    enum truePred = true;
+}
+
+private template attrNamePred(string name)
+{
+    template attrNamePred(A...)
+    {
+        enum attrNamePred = 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 name.
+ */
+template GetAttributes(alias symbol, string attrName)
+{
+    alias GetAttributes!(symbol, attrNamePred!attrName) 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 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[2] == 33);
+
+        alias GetAttributes!(C, "ClassInfo") ciAttrs;
+        static assert(ciAttrs[0].tuple[2] == "version" && ciAttrs[0].tuple[3] == 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[2] == 1 && myAttrs[0].tuple[3] == 2);
+
+        static assert(myAttrs[1].tuple[0] == "MyAttribute");
+        static assert(myAttrs[1].tuple[2] == 3 && myAttrs[1].tuple[3] == 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/d2/qtd/meta/Runtime.d	Thu May 20 15:54:06 2010 +0300
@@ -0,0 +1,292 @@
+/**************************************************************************
+    Copyright: Copyright Max Samukha, 2010
+    Authors: Max Samukha
+    License: Boost Software License 1.0
+**************************************************************************/
+module qtd.meta.Runtime;
+//TODO: Probably replace switch dispatch with pointer dispatch
+//and leave switch dispatch only in C interface
+
+import
+    qtd.meta.Compiletime,
+
+    std.typetuple,
+    std.conv,
+    std.variant,
+    core.sync.rwmutex;
+
+private __gshared ReadWriteMutex lock;
+shared static this()
+{
+    lock = new ReadWriteMutex;
+}
+
+/**
+    IDs of the built-in basic types.
+*/
+enum BasicTypeId
+{
+    ///
+    void_,
+    ///
+    bool_,
+    ///
+    byte_,
+    ///
+    ubyte_,
+    ///
+    short_,
+    ///
+    ushort_,
+    ///
+    int_,
+    ///
+    uint_,
+    ///
+    long_,
+    ///
+    ulong_,
+    ///
+    cent_,
+    ///
+    ucent_,
+    ///
+    float_,
+    ///
+    double_,
+    ///
+    real_,
+    ///
+    ifloat_,
+    ///
+    idouble_,
+    ///
+    ireal_,
+    ///
+    cfloat_,
+    ///
+    cdouble_,
+    ///
+    creal_,
+    ///
+    char_,
+    ///
+    wchar_,
+    ///
+    dchar_
+}
+
+/**
+    Thrown on meta-system errors.
+*/
+class MetaException : Exception
+{
+    this(string msg)
+    {
+        super(msg);
+    }
+}
+
+abstract class Meta
+{
+    alias typeof(this) This;
+
+    string name;
+    MetaAttribute[] attributes;
+    Meta[] members;
+
+    template createImpl(M : This)
+    {
+        static M createImpl(alias symbol)()
+        {
+            auto m = new M;
+            m.construct!symbol;
+            return m;
+        }
+    }
+
+    private void createAttrs(alias symbol)()
+    {
+        alias GetAttributes!symbol attrs;
+        enum len = attrs.length; // COMPILER BUG
+        foreach (i, _; Repeat!(void, len))
+        {
+            alias TypeTuple!(attrs[i].tuple) attr;
+            // if the third element of the attribute data is a MetaAttribute subclass,
+            // use that to create the attribute instance.
+            static if (attr.length > 2 && (is(attr[2] : MetaAttribute)))
+            {
+                alias attr[2] MA;
+                alias TypeTuple!(attr[0..2], attr[3..$]) args;
+                attributes ~= MA /*COMPILER BUG: tuple element as tuple*/[0].create!args();
+            }
+        }
+    }
+
+    protected void construct(alias symbol)()
+    {
+        createAttrs!symbol;
+    }
+}
+
+/**
+    Base class for run time attributes.
+ */
+abstract class MetaAttribute
+{
+    alias typeof(this) This;
+
+    string name;
+    AttributeOptions options;
+
+    This create(string name, AttributeOptions opts, A...)()
+    {
+        auto ma = new This;
+        ma.construct!(name, opts, A)();
+        return ma;
+    }
+
+    void construct(string name, AttributeOptions opts)()
+    {
+        this.name = name;
+        options = opts;
+    }
+}
+
+abstract class MetaType : Meta
+{
+}
+
+abstract class MetaAggregate : MetaType
+{
+}
+
+class MetaClass : MetaAggregate
+{
+    alias typeof(this) This;
+    alias createImpl!This create;
+}
+
+class MetaStruct : MetaAggregate
+{
+    alias typeof(this) This;
+    alias createImpl!This create;
+}
+
+@property
+auto meta(alias symbol, M : Meta)()
+{
+    __gshared static M m;
+
+    {
+        lock.reader.lock;
+        scope(exit)
+            lock.reader.unlock;
+        if (m)
+            return m;
+    }
+
+    lock.writer.lock;
+    scope(exit)
+        lock.writer.unlock;
+
+    if (!m)
+        m = M.create!symbol;
+    return m;
+}
+
+// only classes and structs for now
+@property
+auto meta(T)()
+{
+    static if (is(typeof(T.staticMetaObject)))
+        return T.staticMetaObject;
+    else static if (is(T == class))
+        return meta!(T, MetaClass);
+    else static if (is(T == struct))
+        return meta!(T, MetaStruct);
+    else
+        static assert(false, "No meta object for symbol " ~ T.stringof);
+}
+
+/**
+    A run time attribute implementation that stores the attribute data in an
+    array of variants.
+ */
+class MetaVariantAttribute : MetaAttribute
+{
+    Variant[] values;
+
+    private this()
+    {
+    }
+
+    static MetaVariantAttribute create(string category, AttributeOptions opts, A...)()
+    {
+        auto ret = new MetaVariantAttribute;
+        ret.construct!(category, opts)();
+        foreach(i, _; A)
+        {
+            static if (__traits(compiles, { ret.values ~= Variant(A[i]); } ))
+                ret.values ~= Variant(A[i]);
+        }
+        return ret;
+    }
+}
+
+/**
+    A run time attribute implementation that stores the attribute data in an
+    assiciative array of variants.
+ */
+class MetaVariantDictAttribute : MetaAttribute
+{
+    Variant[string] values;
+    alias typeof(this) This;
+
+    private this()
+    {
+    }
+
+    static This create(string category, AttributeOptions opts, A...)()
+    {
+        auto ret = new This;
+        ret.construct!(category, opts)();
+        foreach(i, _; A)
+        {
+            static if (i % 2 == 0 && __traits(compiles, { ret.values[A[i]] = Variant(A[i + 1]); } ))
+                ret.values[A[i]] = Variant(A[i + 1]); // PHOBOS BUG: phobos asserts on this
+        }
+        return ret;
+    }
+}
+
+version (QtdUnittest)
+{
+    unittest
+    {
+        static void foo() {}
+
+        static class C
+        {
+            mixin InnerAttribute!("variantAttribute", MetaVariantAttribute, "22", foo, 33);
+            mixin InnerAttribute!("variantDictAttribute", MetaVariantDictAttribute,
+                //"a", "33", // PHOBOS BUG: variant is unusable with AAs
+                "b", foo
+                //"c", 44
+                );
+        }
+
+        auto attrs = meta!(C).attributes;
+        assert(attrs.length == 2);
+        auto attr = cast(MetaVariantAttribute)attrs[0];
+
+        assert(attr.name == "variantAttribute");
+        assert(attr.values[0] == "22");
+        assert(attr.values[1] == 33);
+
+        auto attr2 = cast(MetaVariantDictAttribute) attrs[1];
+        assert(attr2.name == "variantDictAttribute");
+        //assert(attr2.values["a"] == "33");
+        //assert(attr2.values["c"] == 44);
+    }
+}
\ No newline at end of file