| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384 | /** * The variant module contains a variant, or polymorphic type. * * Copyright: Copyright (C) 2005-2009 The Tango Team. All rights reserved. * License: BSD style: $(LICENSE) * Authors: Daniel Keep, Sean Kelly */ module tango.core.Variant; private import tango.core.Memory : GC; private import tango.core.Vararg; private import tango.core.Traits; private import tango.core.Tuple; private extern(C) Object _d_toObject(void*); /* * This is to control when we compile in vararg support. Vararg is a complete * pain in the arse. I haven't been able to test under GDC at all (and * support for it may disappear soon anyway) and LDC refuses to build for me. * * As other compilers are tested and verified to work, they should be added * below. It would also probably be a good idea to verify the platforms for * which it works. */ version( DigitalMars ) { version( X86 ) { version( Windows ) { version=EnableVararg; } else version( Posix ) { version=EnableVararg; } } version( X86_64 ) { version( Windows ) { version=EnableVararg; } else version( Posix ) { version=EnableVararg; } version = DigitalMarsX64; import tango.math.Math : max; } } else version( LDC ) { version( X86 ) { version( linux ) { version=EnableVararg; // thanks rawler } } else version( X86_64 ) { version( linux ) { version=EnableVararg; // thanks mwarning } import tango.math.Math : max; } } else version( DDoc ) { // Let's hope DDoc is smart enough to catch this... version=EnableVararg; } version( EnableVararg ) {} else { pragma(msg, "Note: Variant vararg functionality not supported for this " "compiler/platform combination."); pragma(msg, "To override and enable vararg support anyway, compile with " "the EnableVararg version."); } private { /* * This is used to store the actual value being kept in a Variant. */ struct VariantStorage { union { /* * Contains heap-allocated storage for values which are too large * to fit into the Variant directly. */ void[] heap; /* * Used to store arrays directly. Note that this is NOT an actual * array; using a void[] causes the length to change, which screws * up the ptr() property. * * WARNING: this structure MUST match the ABI for arrays for this * platform. AFAIK, all compilers implement arrays this way. * There needs to be a case in the unit test to ensure this. */ struct Array { size_t length; const(void)* ptr; } Array array; // Used to simplify dealing with objects. Object obj; // Used to address storage as an array. ubyte[array.sizeof] data; } /* * This is used to set the array structure safely. We're essentially * just ensuring that if a garbage collection happens mid-assign, we * don't accidentally mark bits of memory we shouldn't. * * Of course, the compiler could always re-order the length and ptr * assignment. Oh well. */ void setArray(const(void)* ptr, size_t length) { array.length = 0; array.ptr = ptr; array.length = length; } } // Determines if the given type is an Object (class) type. template isObject(T) { static if( is( T : Object ) ) const isObject = true; else const isObject = false; } // Determines if the given type is an interface template isInterface(T) { static if( is( T == interface ) ) const isInterface = true; else const isInterface = false; } // A list of all basic types alias Tuple!(bool, char, wchar, dchar, byte, short, int, long, //cent, ubyte, ushort, uint, ulong, //ucent, float, double, real, ifloat, idouble, ireal, cfloat, cdouble, creal) BasicTypes; // see isBasicType template isBasicTypeImpl(T, U) { const isBasicTypeImpl = is( T == U ); } // see isBasicType template isBasicTypeImpl(T, U, Us...) { static if( is( T == U ) ) const isBasicTypeImpl = true; else const isBasicTypeImpl = isBasicTypeImpl!(T, Us); } // Determines if the given type is one of the basic types. template isBasicType(T) { const isBasicType = isBasicTypeImpl!(T, BasicTypes); } /* * Used to determine if we can cast a value of the given TypeInfo to the * specified type implicitly. This should be somewhat faster than the * version in RuntimeTraits since we can basically eliminate half of the * tests. */ bool canImplicitCastToType(dsttypeT)(TypeInfo srctype) { /* * Before we do anything else, we need to "unwrap" typedefs to * get at the real type. While we do that, make sure we don't * accidentally jump over the destination type. */ while( cast(TypeInfo_Typedef) srctype !is null ) { if( srctype is typeid(dsttypeT) ) return true; srctype = cast()srctype.next; } /* * First, we'll generate tests for the basic types. The list of * things which can be cast TO basic types is finite and easily * computed. */ foreach( T ; BasicTypes ) { // If the current type is the target... static if( is( dsttypeT == T ) ) { // ... then for all of the other basic types ... foreach( U ; BasicTypes ) { static if ( // ... if that type is smaller than ... U.sizeof < T.sizeof // ... or the same size and signed-ness ... || ( U.sizeof == T.sizeof && ((isCharType!(T) || isUnsignedIntegerType!(T)) ^ !(isCharType!(U) || isUnsignedIntegerType!(U))) ) ) { // ... test. if( srctype is typeid(U) ) return true; } } // Nothing matched; no implicit casting. return false; } } /* * Account for static arrays being implicitly convertible to dynamic * arrays. */ static if( is( T[] : dsttypeT ) ) { if( typeid(T[]) is srctype ) return true; if( auto ti_sa = cast(TypeInfo_StaticArray) srctype ) return ti_sa.next is typeid(T); return false; } /* * Any pointer can be cast to void*. */ else static if( is( dsttypeT == void* ) ) return (cast(TypeInfo_Pointer) srctype) !is null; /* * Any array can be cast to void[], however remember that it has to * be manually adjusted to preserve the correct length. */ else static if( is( dsttypeT == void[] ) ) return ((cast(TypeInfo_Array) srctype) !is null) || ((cast(TypeInfo_StaticArray) srctype) !is null); else return false; } /* * Aliases itself to the type used to return a value of type T out of a * function. This is basically a work-around for not being able to return * static arrays. */ template returnT(T) { static if( isStaticArrayType!(T) ) alias typeof(T.dup) returnT; else alias T returnT; } /* * Here are some tests that perform runtime versions of the compile-time * traits functions. */ bool isBasicTypeInfo(TypeInfo ti) { foreach( T ; BasicTypes ) if( ti is typeid(T) ) return true; return false; } private import RuntimeTraits = tango.core.RuntimeTraits; alias RuntimeTraits.isStaticArray isStaticArrayTypeInfo; alias RuntimeTraits.isClass isObjectTypeInfo; alias RuntimeTraits.isInterface isInterfaceTypeInfo; } /** * This exception is thrown whenever you attempt to get the value of a Variant * without using a compatible type. */ class VariantTypeMismatchException : Exception { this(TypeInfo expected, TypeInfo got) { super("cannot convert "~expected.toString() ~" value to a "~got.toString()); } } /** * This exception is thrown when you attempt to use an empty Variant with * varargs. */ class VariantVoidVarargException : Exception { this() { super("cannot use Variants containing a void with varargs"); } } /** * 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. * * Example: * ----- * auto v = Variant(42); * ----- */ static Variant opCall(T)(T value) { Variant _this; static if( isStaticArrayType!(T) ) _this = value.dup; else _this = value; return _this; } /** * This pseudo-constructor creates a new Variant using a specified * TypeInfo and raw pointer to the value. * * Params: * type = Type of the value. * ptr = Pointer to the value. * * Returns: * The new Variant. * * Example: * ----- * int life = 42; * auto v = Variant(typeid(typeof(life)), &life); * ----- */ static Variant opCall()(TypeInfo type, void* ptr) { Variant _this; Variant.fromPtr(type, ptr, _this); 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. * * Example: * ----- * Variant v; * v = 42; * ----- */ Variant opAssign(T)(T value) { static if( isStaticArrayType!(T) ) { return (this = value.dup); } else static if( is(T == Variant) ) { type = value.type; this.value = value.value; return this; } else { type = typeid(T); static if( isDynamicArrayType!(T) ) { this.value.setArray(value.ptr, value.length); } else static if( isInterface!(T) ) { this.value.obj = cast(Object) value; } else { /* * If the value is small enough to fit in the storage * available, do so. If it isn't, then make a heap copy. * * Obviously, this pretty clearly breaks value semantics for * large values, but without a postblit operator, there's not * much we can do. :( */ static 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.heap = 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. * * Example: * ----- * auto v = Variant(cast(int) 42); * assert( v.isA!(int) ); * assert( ! v.isA!(short) ); // note no implicit conversion * ----- */ @property 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. * * Example: * ----- * auto v = Variant(cast(int) 42); * assert( v.isA!(int) ); * assert( v.isA!(short) ); // note implicit conversion * ----- */ @property bool isImplicitly(T)() { static if( is( T == class ) || is( T == interface ) ) { // Check for classes and interfaces first. if( cast(TypeInfo_Class) type || cast(TypeInfo_Interface) type ) return (cast(T) value.obj) !is null; else // We're trying to cast TO an object, but we don't have // an object stored. return false; } else { // Test for basic types (oh, and dynamic->static arrays and // pointers.) return ( cast(bool)(typeid(T) is type) || canImplicitCastToType!(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. */ @property 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; } version( DDoc ) { /** * 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. * * Note that attempting to get a statically-sized array will result in a * dynamic array being returned; this is a language limitation. * * Returns: * The value stored within the Variant. */ @property T get(T)() { // For actual implementation, see below. } } else { @property returnT!(S) get(S)() { alias returnT!(S) T; // If we're not dealing with the exact same type as is being // stored, we fail NOW if the type in question isn't an object (we // can let the runtime do the test) and if it isn't something we // know we can implicitly cast to. if( type !is typeid(T) // Let D do runtime check itself && !isObject!(T) && !isInterface!(T) // Allow implicit upcasts && !canImplicitCastToType!(T)(type) ) throw new VariantTypeMismatchException(type,typeid(T)); // Handle basic types, since they account for most of the implicit // casts. static if( isBasicType!(T) ) { if( type is typeid(T) ) { // We got lucky; the types match exactly. If the type is // small, grab it out of storage; otherwise, copy it from // the heap. static if( T.sizeof <= value.sizeof ) return *cast(T*)(&value); else return *cast(T*)(value.heap.ptr); } else { // This handles implicit coercion. What it does is finds // the basic type U which is actually being stored. It // then unpacks the value of type U stored in the Variant // and casts it to type T. // // It is assumed that this is valid to perform since we // should have already eliminated invalid coercions. foreach( U ; BasicTypes ) { if( type is typeid(U) ) { static if( U.sizeof <= value.sizeof ) return cast(T) *cast(U*)(&value); else return cast(T) *cast(U*)(value.heap.ptr); } } throw new VariantTypeMismatchException(type,typeid(T)); } } else static if( isDynamicArrayType!(T) ) { return (cast(typeof(T.ptr)) value.array.ptr) [0..value.array.length]; } else static if( isObject!(T) || isInterface!(T) ) { return cast(T)this.value.obj; } else { static 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.heap)[0..T.sizeof]; return result; } } } } /** * 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. */ auto opBinary(immutable(char)[] op, T)(T rhs) { mixin("return get!(T) " ~ op ~ " rhs;"); } auto opBinaryRight(immutable(char)[] op, T)(T lhs) { mixin("return lhs " ~ op ~ " get!(T);"); } Variant opOpAssign(immutable(char)[] op, T)(T value) { mixin("return (this = get!(T) " ~ op ~ " value);"); } /** * 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; } /* This opCmp is not detectable as one for the purposes of TypeInfo_Struct.xopCmp. * Try doing opCmp(const ref Variant) and opCmp(...). My tests indicate that this may work. */ /// 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(this.ptr); } /** * Returns a string representation of the type being stored in this * Variant. * * Returns: * The string representation of the type contained within the Variant. */ string toString() { return type.toString(); } /** * This can be used to retrieve the TypeInfo for the currently stored * value. */ @property TypeInfo type() { return _type; } /** * This can be used to retrieve a pointer to the value stored in the * variant. */ @property void* ptr() { if( type.tsize <= value.sizeof ) return &value; else return value.heap.ptr; } version( EnableVararg ) { /** * Converts a vararg function argument list into an array of Variants. */ static Variant[] fromVararg(TypeInfo[] types, void* args) { auto vs = new Variant[](types.length); foreach( i, ref v ; vs ) { version(DigitalMarsX64) { scope void[] buffer = new void[types[i].tsize]; va_arg(cast(va_list)args, types[i], buffer.ptr); Variant.fromPtr(types[i], buffer.ptr, v); } else { args = Variant.fromPtr(types[i], args, v); } } return vs; } /// ditto static Variant[] fromVararg(...) { version(DigitalMarsX64) { va_list ap; va_start(ap, __va_argsave); scope (exit) va_end(ap); return Variant.fromVararg(_arguments, ap); } else return Variant.fromVararg(_arguments, _argptr); } /+ /** * Converts an array of Variants into a vararg function argument list. * * This will allocate memory to store the arguments in; you may destroy * this memory when you are done with it if you feel so inclined. */ deprecated static void toVararg(Variant[] vars, out TypeInfo[] types, out va_list args) { // First up, compute the total amount of space we'll need. While // we're at it, work out if any of the values we're storing have // pointers. If they do, we'll need to tell the GC. size_t size = 0; bool noptr = true; foreach( ref v ; vars ) { auto ti = v.type; size += (ti.tsize + size_t.sizeof-1) & ~(size_t.sizeof-1); noptr = noptr && (ti.flags & 2); } // Create the storage, and tell the GC whether it needs to be scanned // or not. auto storage = new ubyte[size]; GC.setAttr(storage.ptr, (GC.getAttr(storage.ptr) & ~GC.BlkAttr.NO_SCAN) | (noptr ? GC.BlkAttr.NO_SCAN : 0)); // Dump the variants into the storage. args = storage.ptr; auto arg_temp = args; types = new TypeInfo[vars.length]; foreach( i, ref v ; vars ) { types[i] = v.type; arg_temp = v.toPtr(arg_temp); } } +/ } // version( EnableVararg ) private: TypeInfo _type = typeid(void); VariantStorage value; @property TypeInfo type(TypeInfo v) { return (_type = v); } /* * Creates a Variant using a given TypeInfo and a void*. Returns the * given pointer adjusted for the next vararg. */ static void* fromPtr(TypeInfo type, void* ptr, out Variant r) { /* * This function basically duplicates the functionality of * opAssign, except that we can't generate code based on the * type of the data we're storing. */ if( type is typeid(void) ) throw new VariantVoidVarargException; r.type = type; if( isStaticArrayTypeInfo(type) ) { /* * Static arrays are passed by-value; for example, if type is * typeid(int[4]), then ptr is a pointer to 16 bytes of memory * (four 32-bit integers). * * It's possible that the memory being pointed to is on the * stack, so we need to copy it before storing it. type.tsize * tells us exactly how many bytes we need to copy. * * Sadly, we can't directly construct the dynamic array version * of type. We'll store the static array type and cope with it * in isImplicitly(S) and get(S). */ r.value.heap = ptr[0 .. type.tsize].dup; } else { if( isObjectTypeInfo(type) || isInterfaceTypeInfo(type) ) { /* * We have to call into the core runtime to turn this pointer * into an actual Object reference. */ r.value.obj = _d_toObject(*cast(void**)ptr); } else { if( type.tsize <= this.value.data.length ) { // Copy into storage r.value.data[0 .. type.tsize] = (cast(ubyte*)ptr)[0 .. type.tsize]; } else { // Store in heap auto buffer = (cast(ubyte*)ptr)[0 .. type.tsize].dup; r.value.heap = cast(void[])buffer; } } } // Compute the "advanced" pointer. return ptr + ( (type.tsize + size_t.sizeof-1) & ~(size_t.sizeof-1) ); } /+version( EnableVararg ) { /* * Takes the current Variant, and dumps its contents into memory pointed * at by a void*, suitable for vararg calls. * * It also returns the supplied pointer adjusted by the size of the data * written to memory. */ void* toPtr(void* ptr) { version( GNU ) { pragma(msg, "WARNING: tango.core.Variant's vararg support has " "not been tested with this compiler." ); } version( LDC ) { pragma(msg, "WARNING: tango.core.Variant's vararg support has " "not been tested with this compiler." ); } if( type is typeid(void) ) throw new VariantVoidVarargException; if( isStaticArrayTypeInfo(type) ) { // Just dump straight ptr[0 .. type.tsize] = this.value.heap[0 .. type.tsize]; } else { if( isInterfaceTypeInfo(type) ) { /* * This is tricky. What we actually have stored in * value.obj is an Object, not an interface. What we * need to do is manually "cast" value.obj to the correct * interface. * * We have the original interface's TypeInfo. This gives us * the interface's ClassInfo. We can also obtain the object's * ClassInfo which contains a list of Interfaces. * * So what we need to do is loop over the interfaces obj * implements until we find the one we're interested in. Then * we just read out the interface's offset and adjust obj * accordingly. */ auto type_i = cast(TypeInfo_Interface) type; bool found = false; foreach( i ; this.value.obj.classinfo.interfaces ) { if( i.classinfo is type_i.info ) { // Found it void* i_ptr = (cast(void*) this.value.obj) + i.offset; *cast(void**)ptr = i_ptr; found = true; break; } } assert(found,"Could not convert Object to interface; " "bad things have happened."); } else { if( type.tsize <= this.value.data.length ) { // Value stored in storage ptr[0 .. type.tsize] = this.value.data[0 .. type.tsize]; } else { // Value stored on heap ptr[0 .. type.tsize] = this.value.heap[0 .. type.tsize]; } } } // Compute the "advanced" pointer. return ptr + ( (type.tsize + size_t.sizeof-1) & ~(size_t.sizeof-1) ); } } // version( EnableVararg ) +/ /* * Performs a type-dependant comparison. Note that this obviously doesn't * take into account things like implicit conversions. */ int opEqualsVariant(Variant rhs) { if( type != rhs.type ) return false; return cast(bool) type.equals(this.ptr, rhs.ptr); } /* * Same as opEqualsVariant except it does opCmp. */ int opCmpVariant(Variant rhs) { if( type != rhs.type ) throw new VariantTypeMismatchException(type, rhs.type); return type.compare(this.ptr, rhs.ptr); } } debug( UnitTest ) { /* * Language tests. */ unittest { { int[2] a; void[] b = a; int[] c = cast(int[]) b; assert( b.length == 2*int.sizeof ); assert( c.length == a.length ); } { struct A { size_t l; void* p; } const(char)[] b = "123"; A a = *cast(A*)(&b); assert( a.l == b.length ); assert( a.p == b.ptr ); } } /* * Basic tests. */ unittest { Variant v; assert( v.isA!(void), v.type.toString() ); assert( v.isEmpty, v.type.toString() ); // Test basic integer storage and implicit casting support 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 ); // Test clearing v.clear(); assert( v.isA!(void), v.type.toString() ); assert( v.isEmpty, v.type.toString() ); // Test strings v = "Hello, World!"c; assert( v.isA!(immutable(char)[]), v.type.toString() ); assert( !v.isImplicitly!(wchar[]), v.type.toString() ); assert( v.get!(immutable(char)[]) == "Hello, World!" ); // Test array storage v = [1,2,3,4,5]; assert( v.isA!(int[]), v.type.toString() ); assert( v.get!(int[]) == [1,2,3,4,5] ); // Make sure arrays are correctly stored so that .ptr works. { int[] a = [1,2,3,4,5]; v = a; auto b = *cast(int[]*)(v.ptr); assert( a.ptr == b.ptr ); assert( a.length == b.length ); } // Test pointer storage v = &v; assert( v.isA!(Variant*), v.type.toString() ); assert( !v.isImplicitly!(int*), v.type.toString() ); assert( v.isImplicitly!(void*), v.type.toString() ); assert( v.get!(Variant*) == &v ); // Test object storage { scope o = new Object; v = o; assert( v.isA!(Object), v.type.toString() ); assert( v.get!(Object) is o ); } // Test interface support { interface A {} interface B : A {} class C : B {} class D : C {} A a = new D; Variant v2 = a; B b = v2.get!(B); C c = v2.get!(C); D d = v2.get!(D); } // Test class/interface implicit casting { class G {} interface H {} class I : G {} class J : H {} struct K {} scope a = new G; scope c = new I; scope d = new J; K e; Variant v2 = a; assert( v2.isImplicitly!(Object), v2.type.toString() ); assert( v2.isImplicitly!(G), v2.type.toString() ); assert(!v2.isImplicitly!(I), v2.type.toString() ); v2 = c; assert( v2.isImplicitly!(Object), v2.type.toString() ); assert( v2.isImplicitly!(G), v2.type.toString() ); assert( v2.isImplicitly!(I), v2.type.toString() ); v2 = d; assert( v2.isImplicitly!(Object), v2.type.toString() ); assert(!v2.isImplicitly!(G), v2.type.toString() ); assert( v2.isImplicitly!(H), v2.type.toString() ); assert( v2.isImplicitly!(J), v2.type.toString() ); v2 = e; assert(!v2.isImplicitly!(Object), v2.type.toString() ); } // Test doubles and implicit casting 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 ); // Test storage transitivity auto u = Variant(v); assert( u.isA!(double), u.type.toString() ); assert( u.get!(double) == 3.1413 ); // Test operators 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" ); // Test op= operators 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" ); // Test comparison 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") ); // Test variants as AA keys { 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 ); } // Test AA storage { 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 ); } } /* * Vararg tests. */ version( EnableVararg ) { private import tango.core.Vararg; unittest { class A { @property const(char)[] msg() { return "A"; } } class B : A { @property override const(char)[] msg() { return "B"; } } interface C { @property const(char)[] name(); } class D : B, C { @property override const(char)[] msg() { return "D"; } @property override const(char)[] name() { return "phil"; } } struct S { int a, b, c, d; } Variant[] scoop(...) { version(DigitalMarsX64) { va_list ap; va_start(ap, __va_argsave); scope (exit) va_end(ap); return Variant.fromVararg(_arguments, ap); } return Variant.fromVararg(_arguments, _argptr); } auto va_0 = cast(char) '?'; auto va_1 = cast(short) 42; auto va_2 = cast(int) 1701; auto va_3 = cast(long) 9001; auto va_4 = cast(float) 3.14; auto va_5 = cast(double)2.14; auto va_6 = cast(real) 0.1; auto va_7 = "abcd"[]; S va_8 = { 1, 2, 3, 4 }; A va_9 = new A; B va_a = new B; C va_b = new D; D va_c = new D; auto vs = scoop(va_0, va_1, va_2, va_3, va_4, va_5, va_6, va_7, va_8, va_9, va_a, va_b, va_c); assert( vs[0x0].get!(typeof(va_0)) == va_0 ); assert( vs[0x1].get!(typeof(va_1)) == va_1 ); assert( vs[0x2].get!(typeof(va_2)) == va_2 ); assert( vs[0x3].get!(typeof(va_3)) == va_3 ); assert( vs[0x4].get!(typeof(va_4)) == va_4 ); assert( vs[0x5].get!(typeof(va_5)) == va_5 ); assert( vs[0x6].get!(typeof(va_6)) == va_6 ); assert( vs[0x7].get!(typeof(va_7)) == va_7 ); assert( vs[0x8].get!(typeof(va_8)) == va_8 ); assert( vs[0x9].get!(typeof(va_9)) is va_9 ); assert( vs[0xa].get!(typeof(va_a)) is va_a ); assert( vs[0xb].get!(typeof(va_b)) is va_b ); assert( vs[0xc].get!(typeof(va_c)) is va_c ); assert( vs[0x9].get!(typeof(va_9)).msg == "A" ); assert( vs[0xa].get!(typeof(va_a)).msg == "B" ); assert( vs[0xc].get!(typeof(va_c)).msg == "D" ); assert( vs[0xb].get!(typeof(va_b)).name == "phil" ); assert( vs[0xc].get!(typeof(va_c)).name == "phil" ); /+ version (none) version(X86) // TODO toVararg won't work in x86_64 as it is now { TypeInfo[] types; void* args; Variant.toVararg(vs, types, args); assert( types[0x0] is typeid(typeof(va_0)) ); assert( types[0x1] is typeid(typeof(va_1)) ); assert( types[0x2] is typeid(typeof(va_2)) ); assert( types[0x3] is typeid(typeof(va_3)) ); assert( types[0x4] is typeid(typeof(va_4)) ); assert( types[0x5] is typeid(typeof(va_5)) ); assert( types[0x6] is typeid(typeof(va_6)) ); assert( types[0x7] is typeid(typeof(va_7)) ); assert( types[0x8] is typeid(typeof(va_8)) ); assert( types[0x9] is typeid(typeof(va_9)) ); assert( types[0xa] is typeid(typeof(va_a)) ); assert( types[0xb] is typeid(typeof(va_b)) ); assert( types[0xc] is typeid(typeof(va_c)) ); auto ptr = args; auto vb_0 = va_arg!(typeof(va_0))(ptr); auto vb_1 = va_arg!(typeof(va_1))(ptr); auto vb_2 = va_arg!(typeof(va_2))(ptr); auto vb_3 = va_arg!(typeof(va_3))(ptr); auto vb_4 = va_arg!(typeof(va_4))(ptr); auto vb_5 = va_arg!(typeof(va_5))(ptr); auto vb_6 = va_arg!(typeof(va_6))(ptr); auto vb_7 = va_arg!(typeof(va_7))(ptr); auto vb_8 = va_arg!(typeof(va_8))(ptr); auto vb_9 = va_arg!(typeof(va_9))(ptr); auto vb_a = va_arg!(typeof(va_a))(ptr); auto vb_b = va_arg!(typeof(va_b))(ptr); auto vb_c = va_arg!(typeof(va_c))(ptr); assert( vb_0 == va_0 ); assert( vb_1 == va_1 ); assert( vb_2 == va_2 ); assert( vb_3 == va_3 ); assert( vb_4 == va_4 ); assert( vb_5 == va_5 ); assert( vb_6 == va_6 ); assert( vb_7 == va_7 ); assert( vb_8 == va_8 ); assert( vb_9 is va_9 ); assert( vb_a is va_a ); assert( vb_b is va_b ); assert( vb_c is va_c ); assert( vb_9.msg == "A" ); assert( vb_a.msg == "B" ); assert( vb_c.msg == "D" ); assert( vb_b.name == "phil" ); assert( vb_c.name == "phil" ); } +/ } } } |