view d2/qtd/meta/Runtime.d @ 364:a084e2df3776

Preparing for non-QObject meta-objects. Now meta-objects for static types can be uniformly accessed using meta!T
author Max Samukha <maxter@maxter.com>
date Fri, 11 Jun 2010 12:16:09 +0300
parents 59d847a814e3
children 958e8b9a89bd
line wrap: on
line source

/**************************************************************************
    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.Core,
    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 MetaBase
{
    alias typeof(this) This;

    string name;
    MetaAttribute[] attributes;
    MetaBase[] 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 : MetaBase
{
}

abstract class MetaAggregate : MetaType
{
}

class MetaClass : MetaAggregate
{
    alias typeof(this) This;

private:
    This base_;
    This firstDerived_;
    This next_;
    TypeInfo_Class classInfo_;

    this() {}

public:
    /**
        Returns the meta-object of the base class.
     */
    @property
    This base()
    {
        return base_;
    }

    /**
        Returns next meta-object on this level of the derivation hierarchy.
     */
    @property
    This next()
    {
        return next_;
    }

    /**
        Returns meta-object of the first derived class.
     */
    @property
    This firstDerived()
    {
        return firstDerived_;
    }

    /**
        D class info.
     */
    @property
    TypeInfo_Class classInfo()
    {
        return classInfo_;
    }

    /* internal */ alias createImpl!This create;

    /* internal */ void construct(T : Object)()
    {
        static if (!is(T == Object))
        {
            alias BaseClassesTuple!(T)[0] Base;
            base_ = meta!Base;
        }
        classInfo_ = T.classinfo;
    }
}

class MetaStruct : MetaAggregate
{
    alias typeof(this) This;
    alias createImpl!This create;
}

@property
auto meta(alias symbol, M : MetaBase)()
{
    __gshared static M sharedM;
    static M m;

    if (!m)
    {
        synchronized(qtdMoLock)
        {
            if (!sharedM)
                sharedM = M.create!symbol;
        }
        m = sharedM;
    }

    return m;
}

// only classes and structs for now
@property
auto meta(T)()
{
    static if (is(T.Meta)) // If the type defines a meta-class - use that.
        return meta!(T, T.Meta);
    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);
    }
}