diff tango/tango/core/Variant.d @ 132:1700239cab2e trunk

[svn r136] MAJOR UNSTABLE UPDATE!!! Initial commit after moving to Tango instead of Phobos. Lots of bugfixes... This build is not suitable for most things.
author lindquist
date Fri, 11 Jan 2008 17:57:40 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tango/tango/core/Variant.d	Fri Jan 11 17:57:40 2008 +0100
@@ -0,0 +1,677 @@
+/**
+ * The variant module contains a variant, or polymorphic type.
+ *
+ * Copyright: Copyright (C) 2005-2007 The Tango Team.  All rights reserved.
+ * License:   BSD style: $(LICENSE)
+ * Authors:   Daniel Keep, Sean Kelly
+ */
+module tango.core.Variant;
+
+private import tango.core.Exception : TracedException;
+private import tango.core.Vararg : va_list;
+
+private
+{
+    template maxT(uint a, uint b)
+    {
+        const maxT = (a > b) ? a : b;
+    }
+
+    struct AtomicTypes
+    {
+        union
+        {
+            bool _bool;
+            char _char;
+            wchar _wchar;
+            dchar _dchar;
+            byte _byte;
+            short _short;
+            int _int;
+            long _long;
+            ubyte _ubyte;
+            ushort _ushort;
+            uint _uint;
+            ulong _ulong;
+            float _float;
+            double _double;
+            real _real;
+            ifloat _ifloat;
+            idouble _idouble;
+            ireal _ireal;
+            void* ptr;
+            void[] arr;
+            Object obj;
+            ubyte[maxT!(_real.sizeof,arr.sizeof)] data;
+        }
+    }
+
+    template isAtomicType(T)
+    {
+        static if( is( T == bool )
+                || is( T == char )
+                || is( T == wchar )
+                || is( T == dchar )
+                || is( T == byte )
+                || is( T == short )
+                || is( T == int )
+                || is( T == long )
+                || is( T == ubyte )
+                || is( T == ushort )
+                || is( T == uint )
+                || is( T == ulong )
+                || is( T == float )
+                || is( T == double )
+                || is( T == real )
+                || is( T == ifloat )
+                || is( T == idouble )
+                || is( T == ireal ) )
+            const isAtomicType = true;
+        else
+            const isAtomicType = false;
+    }
+
+    template isArray(T)
+    {
+        static if( is( T U : U[] ) )
+            const isArray = true;
+        else
+            const isArray = false;
+    }
+
+    template isPointer(T)
+    {
+        static if( is( T U : U* ) )
+            const isPointer = true;
+        else
+            const isPointer = false;
+    }
+
+    template isObject(T)
+    {
+        static if( is( T : Object ) )
+            const isObject = true;
+        else
+            const isObject = false;
+    }
+
+    template isStaticArray(T)
+    {
+        static if( is( typeof(T.init)[(T).sizeof / typeof(T.init).sizeof] == T ) )
+            const isStaticArray = true;
+        else
+            const isStaticArray = false;
+    }
+
+    bool isAny(T,argsT...)(T v, argsT args)
+    {
+        foreach( arg ; args )
+            if( v is arg ) return true;
+        return false;
+    }
+
+    const tibool = typeid(bool);
+    const tichar = typeid(char);
+    const tiwchar = typeid(wchar);
+    const tidchar = typeid(dchar);
+    const tibyte = typeid(byte);
+    const tishort = typeid(short);
+    const tiint = typeid(int);
+    const tilong = typeid(long);
+    const tiubyte = typeid(ubyte);
+    const tiushort = typeid(ushort);
+    const tiuint = typeid(uint);
+    const tiulong = typeid(ulong);
+    const tifloat = typeid(float);
+    const tidouble = typeid(double);
+    const tireal = typeid(real);
+    const tiifloat = typeid(ifloat);
+    const tiidouble = typeid(idouble);
+    const tiireal = typeid(ireal);
+
+    bool canImplicitCastTo(dsttypeT)(TypeInfo srctype)
+    {
+        static if( is( dsttypeT == char ) )
+            return isAny(srctype, tibool, tiubyte);
+
+        else static if( is( dsttypeT == wchar ) )
+            return isAny(srctype, tibool, tiubyte, tiushort, tichar);
+
+        else static if( is( dsttypeT == dchar ) )
+            return isAny(srctype, tibool, tiubyte, tiushort, tiuint, tichar,
+                    tiwchar);
+
+        else static if( is( dsttypeT == byte ) )
+            return isAny(srctype, tibool);
+
+        else static if( is( dsttypeT == ubyte ) )
+            return isAny(srctype, tibool, tichar);
+
+        else static if( is( dsttypeT == short ) )
+            return isAny(srctype, tibool, tibyte, tiubyte, tichar);
+
+        else static if( is( dsttypeT == ushort ) )
+            return isAny(srctype, tibool, tibyte, tiubyte, tichar, tiwchar);
+
+        else static if( is( dsttypeT == int ) )
+            return isAny(srctype, tibool, tibyte, tiubyte, tishort, tiushort,
+                    tichar, tiwchar);
+
+        else static if( is( dsttypeT == uint ) )
+            return isAny(srctype, tibool, tibyte, tiubyte, tishort, tiushort,
+                    tichar, tiwchar, tidchar);
+
+        else static if( is( dsttypeT == long ) || is( dsttypeT == ulong ) )
+            return isAny(srctype, tibool, tibyte, tiubyte, tishort, tiushort,
+                        tiint, tiuint, tichar, tiwchar, tidchar);
+
+        else static if( is( dsttypeT == float ) )
+            return isAny(srctype, tibool, tibyte, tiubyte);
+
+        else static if( is( dsttypeT == double ) )
+            return isAny(srctype, tibool, tibyte, tiubyte, tifloat);
+
+        else static if( is( dsttypeT == real ) )
+            return isAny(srctype, tibool, tibyte, tiubyte, tifloat, tidouble);
+
+        else static if( is( dsttypeT == idouble ) )
+            return isAny(srctype, tiifloat);
+
+        else static if( is( dsttypeT == ireal ) )
+            return isAny(srctype, tiifloat, tiidouble);
+
+        else
+            return false;
+    }
+
+    template storageT(T)
+    {
+        static if( isStaticArray!(T) )
+            alias typeof(T.dup) storageT;
+        else
+            alias T storageT;
+    }
+}
+
+/**
+ * This exception is thrown whenever you attempt to get the value of a Variant
+ * without using a compatible type.
+ */
+class VariantTypeMismatchException : TracedException
+{
+    this(TypeInfo expected, TypeInfo got)
+    {
+        super("cannot convert "~expected.toString
+                    ~" value to a "~got.toString);
+    }
+}
+
+/**
+ * The Variant type is used to dynamically store values of different types at
+ * runtime.
+ *
+ * You can create a Variant using either the pseudo-constructor or direct
+ * assignment.
+ *
+ * -----
+ *  Variant v = Variant(42);
+ *  v = "abc";
+ * -----
+ */
+struct Variant
+{
+    /**
+     * This pseudo-constructor is used to place a value into a new Variant.
+     *
+     * Params:
+     *  value = The value you wish to put in the Variant.
+     *
+     * Returns:
+     *  The new Variant.
+     */
+    static Variant opCall(T)(T value)
+    {
+        Variant _this;
+
+        static if( isStaticArray!(T) )
+            _this = value.dup;
+
+        else
+            _this = value;
+
+        return _this;
+    }
+
+    /**
+     * This operator allows you to assign arbitrary values directly into an
+     * existing Variant.
+     *
+     * Params:
+     *  value = The value you wish to put in the Variant.
+     *
+     * Returns:
+     *  The new value of the assigned-to variant.
+     */
+    Variant opAssign(T)(T value)
+    {
+        static if( isStaticArray!(T) )
+        {
+            return (*this = value.dup);
+        }
+        else
+        {
+            type = typeid(T);
+
+            static if( isAtomicType!(T) )
+            {
+                mixin("this.value._"~T.stringof~"=value;");
+            }
+            else static if( isArray!(T) )
+            {
+                this.value.arr = (cast(void*)value.ptr)
+                    [0 .. value.length];
+            }
+            else static if( isPointer!(T) )
+            {
+                this.value.ptr = cast(void*)T;
+            }
+            else static if( isObject!(T) )
+            {
+                this.value.obj = T;
+            }
+            else
+            {
+                if( T.sizeof <= this.value.data.length )
+                {
+                    this.value.data[0..T.sizeof] =
+                        (cast(ubyte*)&value)[0..T.sizeof];
+                }
+                else
+                {
+                    auto buffer = (cast(ubyte*)&value)[0..T.sizeof].dup;
+                    this.value.arr = cast(void[])buffer;
+                }
+            }
+            return *this;
+        }
+    }
+
+    /**
+     * This member can be used to determine if the value stored in the Variant
+     * is of the specified type.  Note that this comparison is exact: it does
+     * not take implicit casting rules into account.
+     *
+     * Returns:
+     *  true if the Variant contains a value of type T, false otherwise.
+     */
+    bool isA(T)()
+    {
+        return cast(bool)(typeid(T) is type);
+    }
+
+    /**
+     * This member can be used to determine if the value stored in the Variant
+     * is of the specified type.  This comparison attempts to take implicit
+     * conversion rules into account.
+     *
+     * Returns:
+     *  true if the Variant contains a value of type T, or if the Variant
+     *  contains a value that can be implicitly cast to type T; false
+     *  otherwise.
+     */
+    bool isImplicitly(T)()
+    {
+        return ( cast(bool)(typeid(T) is type)
+                || canImplicitCastTo!(T)(type) );
+    }
+
+    /**
+     * This determines whether the Variant has an assigned value or not.  It
+     * is simply short-hand for calling the isA member with a type of void.
+     *
+     * Returns:
+     *  true if the Variant does not contain a value, false otherwise.
+     */
+    bool isEmpty()
+    {
+        return isA!(void);
+    }
+
+    /**
+     * This member will clear the Variant, returning it to an empty state.
+     */
+    void clear()
+    {
+        _type = typeid(void);
+        value = value.init;
+    }
+
+    /**
+     * This is the primary mechanism for extracting a value from a Variant.
+     * Given a destination type S, it will attempt to extract the value of the
+     * Variant into that type.  If the value contained within the Variant
+     * cannot be implicitly cast to the given type S, it will throw an
+     * exception.
+     *
+     * You can check to see if this operation will fail by calling the
+     * isImplicitly member with the type S.
+     *
+     * Returns:
+     *  The value stored within the Variant.
+     */
+    storageT!(S) get(S)()
+    {
+        alias storageT!(S) T;
+
+        if( type !is typeid(T)
+                // Let D do runtime check itself
+                && !isObject!(T)
+                // Allow implicit upcasts
+                && !canImplicitCastTo!(T)(type)
+          )
+            throw new VariantTypeMismatchException(type,typeid(T));
+
+        static if( isAtomicType!(T) )
+        {
+            if( type is typeid(T) )
+            {
+                return mixin("this.value._"~T.stringof);
+            }
+            else
+            {
+                if( type is tibool ) return cast(T)this.value._bool;
+                else if( type is tichar ) return cast(T)this.value._char;
+                else if( type is tiwchar ) return cast(T)this.value._wchar;
+                else if( type is tidchar ) return cast(T)this.value._dchar;
+                else if( type is tibyte ) return cast(T)this.value._byte;
+                else if( type is tishort ) return cast(T)this.value._short;
+                else if( type is tiint ) return cast(T)this.value._int;
+                else if( type is tilong ) return cast(T)this.value._long;
+                else if( type is tiubyte ) return cast(T)this.value._ubyte;
+                else if( type is tiushort ) return cast(T)this.value._ushort;
+                else if( type is tiuint ) return cast(T)this.value._uint;
+                else if( type is tiulong ) return cast(T)this.value._ulong;
+                else if( type is tifloat ) return cast(T)this.value._float;
+                else if( type is tidouble ) return cast(T)this.value._double;
+                else if( type is tireal ) return cast(T)this.value._real;
+                else if( type is tiifloat ) return cast(T)this.value._ifloat;
+                else if( type is tiidouble ) return cast(T)this.value._idouble;
+                else if( type is tiireal ) return cast(T)this.value._ireal;
+                else
+                    throw new VariantTypeMismatchException(type,typeid(T));
+            }
+        }
+        else static if( isArray!(T) )
+        {
+            return (cast(typeof(T[0])*)this.value.arr.ptr)
+                [0 .. this.value.arr.length];
+        }
+        else static if( isPointer!(T) )
+        {
+            return cast(T)this.value.ptr;
+        }
+        else static if( isObject!(T) )
+        {
+            return cast(T)this.value.obj;
+        }
+        else
+        {
+            if( T.sizeof <= this.value.data.length )
+            {
+                T result;
+                (cast(ubyte*)&result)[0..T.sizeof] =
+                    this.value.data[0..T.sizeof];
+                return result;
+            }
+            else
+            {
+                T result;
+                (cast(ubyte*)&result)[0..T.sizeof] =
+                    (cast(ubyte[])this.value.arr)[0..T.sizeof];
+                return result;
+            }
+        }
+        assert(false);
+    }
+
+    /**
+     * The following operator overloads are defined for the sake of
+     * convenience.  It is important to understand that they do not allow you
+     * to use a Variant as both the left-hand and right-hand sides of an
+     * expression.  One side of the operator must be a concrete type in order
+     * for the Variant to know what code to generate.
+     */
+    typeof(T+T) opAdd(T)(T rhs)     { return get!(T) + rhs; }
+    typeof(T+T) opAdd_r(T)(T lhs)   { return lhs + get!(T); } /// ditto
+    typeof(T-T) opSub(T)(T rhs)     { return get!(T) - rhs; } /// ditto
+    typeof(T-T) opSub_r(T)(T lhs)   { return lhs - get!(T); } /// ditto
+    typeof(T*T) opMul(T)(T rhs)     { return get!(T) * rhs; } /// ditto
+    typeof(T*T) opMul_r(T)(T lhs)   { return lhs * get!(T); } /// ditto
+    typeof(T/T) opDiv(T)(T rhs)     { return get!(T) / rhs; } /// ditto
+    typeof(T/T) opDiv_r(T)(T lhs)   { return lhs / get!(T); } /// ditto
+    typeof(T%T) opMod(T)(T rhs)     { return get!(T) % rhs; } /// ditto
+    typeof(T%T) opMod_r(T)(T lhs)   { return lhs % get!(T); } /// ditto
+    typeof(T&T) opAnd(T)(T rhs)     { return get!(T) & rhs; } /// ditto
+    typeof(T&T) opAnd_r(T)(T lhs)   { return lhs & get!(T); } /// ditto
+    typeof(T|T) opOr(T)(T rhs)      { return get!(T) | rhs; } /// ditto
+    typeof(T|T) opOr_r(T)(T lhs)    { return lhs | get!(T); } /// ditto
+    typeof(T^T) opXor(T)(T rhs)     { return get!(T) ^ rhs; } /// ditto
+    typeof(T^T) opXor_r(T)(T lhs)   { return lhs ^ get!(T); } /// ditto
+    typeof(T<<T) opShl(T)(T rhs)    { return get!(T) << rhs; } /// ditto
+    typeof(T<<T) opShl_r(T)(T lhs)  { return lhs << get!(T); } /// ditto
+    typeof(T>>T) opShr(T)(T rhs)    { return get!(T) >> rhs; } /// ditto
+    typeof(T>>T) opShr_r(T)(T lhs)  { return lhs >> get!(T); } /// ditto
+    typeof(T>>>T) opUShr(T)(T rhs)  { return get!(T) >>> rhs; } /// ditto
+    typeof(T>>>T) opUShr_r(T)(T lhs){ return lhs >>> get!(T); } /// ditto
+    typeof(T~T) opCat(T)(T rhs)     { return get!(T) ~ rhs; } /// ditto
+    typeof(T~T) opCat_r(T)(T lhs)   { return lhs ~ get!(T); } /// ditto
+
+    Variant opAddAssign(T)(T value) { return (*this = get!(T) + value); } /// ditto
+    Variant opSubAssign(T)(T value) { return (*this = get!(T) - value); } /// ditto
+    Variant opMulAssign(T)(T value) { return (*this = get!(T) * value); } /// ditto
+    Variant opDivAssign(T)(T value) { return (*this = get!(T) / value); } /// ditto
+    Variant opModAssign(T)(T value) { return (*this = get!(T) % value); } /// ditto
+    Variant opAndAssign(T)(T value) { return (*this = get!(T) & value); } /// ditto
+    Variant opOrAssign(T)(T value)  { return (*this = get!(T) | value); } /// ditto
+    Variant opXorAssign(T)(T value) { return (*this = get!(T) ^ value); } /// ditto
+    Variant opShlAssign(T)(T value) { return (*this = get!(T) << value); } /// ditto
+    Variant opShrAssign(T)(T value) { return (*this = get!(T) >> value); } /// ditto
+    Variant opUShrAssign(T)(T value){ return (*this = get!(T) >>> value); } /// ditto
+    Variant opCatAssign(T)(T value) { return (*this = get!(T) ~ value); } /// ditto
+
+    /**
+     * The following operators can be used with Variants on both sides.  Note
+     * that these operators do not follow the standard rules of
+     * implicit conversions.
+     */
+    int opEquals(T)(T rhs)
+    {
+        static if( is( T == Variant ) )
+            return opEqualsVariant(rhs);
+        else
+            return get!(T) == rhs;
+    }
+
+    /// ditto
+    int opCmp(T)(T rhs)
+    {
+        static if( is( T == Variant ) )
+            return opCmpVariant(rhs);
+        else
+        {
+            auto lhs = get!(T);
+            return (lhs < rhs) ? -1 : (lhs == rhs) ? 0 : 1;
+        }
+    }
+
+    /// ditto
+    hash_t toHash()
+    {
+        return type.getHash(data.ptr);
+    }
+
+    /**
+     * Performs "stringification" of the value stored within the Variant.  In
+     * the case of the Variant having no assigned value, it will return the
+     * string "Variant.init".
+     *
+     * Returns:
+     *  The string representation of the value contained within the Variant.
+     */
+    char[] toString()
+    {
+        return type.toString;
+    }
+
+    /**
+     * This can be used to retrieve the TypeInfo for the currently stored
+     * value.
+     */
+    TypeInfo type()
+    {
+        return _type;
+    }
+
+private:
+    TypeInfo _type = typeid(void);
+    AtomicTypes value;
+
+    TypeInfo type(TypeInfo v)
+    {
+        return (_type = v);
+    }
+
+    int opEqualsVariant(Variant rhs)
+    {
+        if( type != rhs.type ) return false;
+        return cast(bool) type.equals(data.ptr, rhs.data.ptr);
+    }
+
+    int opCmpVariant(Variant rhs)
+    {
+        if( type != rhs.type )
+            throw new VariantTypeMismatchException(type, rhs.type);
+        return type.compare(data.ptr, rhs.data.ptr);
+    }
+
+    void[] data()
+    {
+        if( type.tsize <= value.data.length )
+            return cast(void[])(value.data);
+        else
+            return value.arr;
+    }
+}
+
+debug( UnitTest )
+{
+    unittest
+    {
+        Variant v;
+        assert( v.isA!(void), v.type.toString );
+        assert( v.isEmpty, v.type.toString );
+
+        v = 42;
+        assert( v.isA!(int), v.type.toString );
+        assert( v.isImplicitly!(long), v.type.toString );
+        assert( v.isImplicitly!(ulong), v.type.toString );
+        assert( !v.isImplicitly!(uint), v.type.toString );
+        assert( v.get!(int) == 42 );
+        assert( v.get!(long) == 42L );
+        assert( v.get!(ulong) == 42uL );
+
+        v.clear;
+        assert( v.isA!(void), v.type.toString );
+        assert( v.isEmpty, v.type.toString );
+
+        v = "Hello, World!"c;
+        assert( v.isA!(char[]), v.type.toString );
+        assert( !v.isImplicitly!(wchar[]), v.type.toString );
+        assert( v.get!(char[]) == "Hello, World!" );
+
+        v = [1,2,3,4,5];
+        assert( v.isA!(int[]), v.type.toString );
+        assert( v.get!(int[]) == [1,2,3,4,5] );
+
+        v = 3.1413;
+        assert( v.isA!(double), v.type.toString );
+        assert( v.isImplicitly!(real), v.type.toString );
+        assert( !v.isImplicitly!(float), v.type.toString );
+        assert( v.get!(double) == 3.1413 );
+        
+        auto u = Variant(v);
+        assert( u.isA!(double), u.type.toString );
+        assert( u.get!(double) == 3.1413 );
+
+        v = 38;
+        assert( v + 4 == 42 );
+        assert( 4 + v == 42 );
+        assert( v - 4 == 34 );
+        assert( 4 - v == -34 );
+        assert( v * 2 == 76 );
+        assert( 2 * v == 76 );
+        assert( v / 2 == 19 );
+        assert( 2 / v == 0 );
+        assert( v % 2 == 0 );
+        assert( 2 % v == 2 );
+        assert( (v & 6) == 6 );
+        assert( (6 & v) == 6 );
+        assert( (v | 9) == 47 );
+        assert( (9 | v) == 47 );
+        assert( (v ^ 5) == 35 );
+        assert( (5 ^ v) == 35 );
+        assert( v << 1 == 76 );
+        assert( 1 << Variant(2) == 4 );
+        assert( v >> 1 == 19 );
+        assert( 4 >> Variant(2) == 1 );
+
+        assert( Variant("abc") ~ "def" == "abcdef" );
+        assert( "abc" ~ Variant("def") == "abcdef" );
+
+        v = 38; v += 4; assert( v == 42 );
+        v = 38; v -= 4; assert( v == 34 );
+        v = 38; v *= 2; assert( v == 76 );
+        v = 38; v /= 2; assert( v == 19 );
+        v = 38; v %= 2; assert( v == 0 );
+        v = 38; v &= 6; assert( v == 6 );
+        v = 38; v |= 9; assert( v == 47 );
+        v = 38; v ^= 5; assert( v == 35 );
+        v = 38; v <<= 1; assert( v == 76 );
+        v = 38; v >>= 1; assert( v == 19 );
+
+        v = "abc"; v ~= "def"; assert( v == "abcdef" );
+
+        assert( Variant(0) < Variant(42) );
+        assert( Variant(42) > Variant(0) );
+        assert( Variant(21) == Variant(21) );
+        assert( Variant(0) != Variant(42) );
+        assert( Variant("bar") == Variant("bar") );
+        assert( Variant("foo") != Variant("bar") );
+        {
+            auto v1 = Variant(42);
+            auto v2 = Variant("foo");
+            auto v3 = Variant(1+2.0i);
+
+            int[Variant] hash;
+            hash[v1] = 0;
+            hash[v2] = 1;
+            hash[v3] = 2;
+            
+            assert( hash[v1] == 0 );
+            assert( hash[v2] == 1 );
+            assert( hash[v3] == 2 );
+        }
+        {
+            int[char[]] hash;
+            hash["a"] = 1;
+            hash["b"] = 2;
+            hash["c"] = 3;
+            Variant vhash = hash;
+
+            assert( vhash.get!(int[char[]])["a"] == 1 );
+            assert( vhash.get!(int[char[]])["b"] == 2 );
+            assert( vhash.get!(int[char[]])["c"] == 3 );
+        }
+    }
+}
+