view d2/qtd/meta/Runtime.d @ 365:958e8b9a89bd

Changeset a084e2df3776 is broken. Backing out.
author Max Samukha <maxter@spambox.com>
date Fri, 11 Jun 2010 20:09:25 +0300
parents a084e2df3776
children 185df9220ea7
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.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);
    }
}