// Written in the D programming language. /**This module implements a$(HTTP erdani.org/publications/cuj-04-2002.php.html,discriminated union)type (a.k.a.$(HTTP en.wikipedia.org/wiki/Tagged_union,tagged union),$(HTTP en.wikipedia.org/wiki/Algebraic_data_type,algebraic type)).Such types are usefulfor type-uniform binary interfaces, interfacing with scriptinglanguages, and comfortable exploratory programming. A $(LREF Variant) object can hold a value of any type, with very fewrestrictions (such as `shared` types and noncopyable types). Setting the valueis as immediate as assigning to the `Variant` object. To read back the value ofthe appropriate type `T`, use the $(LREF get) method. To query whether a`Variant` currently holds a value of type `T`, use $(LREF peek). To fetch theexact type currently held, call $(LREF type), which returns the `TypeInfo` ofthe current value. In addition to $(LREF Variant), this module also defines the $(LREF Algebraic)type constructor. Unlike `Variant`, `Algebraic` only allows a finite set oftypes, which are specified in the instantiation (e.g. $(D Algebraic!(int,string)) may only hold an `int` or a `string`). $(RED Warning: $(LREF Algebraic) is outdated and not recommended for use in newcode. Instead, use $(REF SumType, std,sumtype).) Credits: Reviewed by Brad Roberts. Daniel Keep provided a detailed code reviewprompting the following improvements: (1) better support for arrays; (2) supportfor associative arrays; (3) friendlier behavior towards the garbage collector.Copyright: Copyright Andrei Alexandrescu 2007 - 2015.License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).Authors: $(HTTP erdani.org, Andrei Alexandrescu)Source: $(PHOBOSSRC std/variant.d)*/module std.variant; import std.meta, std.traits, std.typecons;import core.memory : GC; ///@system unittest{ Variant a; // Must assign before use, otherwise exception ensues // Initialize with an integer; make the type int Variant b = 42; assert(b.type == typeid(int)); // Peek at the value assert(b.peek!(int) !is null && *b.peek!(int) == 42); // Automatically convert per language rules auto x = b.get!(real); // Assign any other type, including other variants a = b; a = 3.14; assert(a.type == typeid(double)); // Implicit conversions work just as with built-in types assert(a < b); // Check for convertibility assert(!a.convertsTo!(int)); // double not convertible to int // Strings and all other arrays are supported a = "now I'm a string"; assert(a == "now I'm a string"); // can also assign arrays a = new int[42]; assert(a.length == 42); a[5] = 7; assert(a[5] == 7); // Can also assign class values class Foo {} auto foo = new Foo; a = foo; assert(*a.peek!(Foo) == foo); // and full type information is preserved} /++ Gives the `sizeof` the largest type given. See_Also: $(LINK https://forum.dlang.org/thread/wbpnncxepehgcswhuazl@forum.dlang.org?page=1) +/template maxSize(Ts...){ align(1) union Impl { static foreach (i, T; Ts) { static if (!is(T == void)) mixin("T _field_", i, ";"); } } enum maxSize = Impl.sizeof;} ///@safe unittest{ struct Cat { int a, b, c; } align(1) struct S { long l; ubyte b; } align(1) struct T { ubyte b; long l; } static assert(maxSize!(int, long) == 8); static assert(maxSize!(bool, byte) == 1); static assert(maxSize!(bool, Cat) == 12); static assert(maxSize!(char) == 1); static assert(maxSize!(char, short, ubyte) == 2); static assert(maxSize!(char, long, ubyte) == 8); import std.algorithm.comparison : max; static assert(maxSize!(long, S) == max(long.sizeof, S.sizeof)); static assert(maxSize!(S, T) == max(S.sizeof, T.sizeof)); static assert(maxSize!(int, ubyte[7]) == 7); static assert(maxSize!(int, ubyte[3]) == 4); static assert(maxSize!(int, int, ubyte[3]) == 4); static assert(maxSize!(void, int, ubyte[3]) == 4); static assert(maxSize!(void) == 1);} struct This; private alias This2Variant(V, T...) = AliasSeq!(ReplaceTypeUnless!(isAlgebraic, This, V, T)); // We can't just use maxAlignment because no types might be specified// to VariantN, so handle that here and then pass along the rest.private template maxVariantAlignment(U...)if (isTypeTuple!U){ static if (U.length == 0) { import std.algorithm.comparison : max; enum maxVariantAlignment = max(real.alignof, size_t.alignof); } else enum maxVariantAlignment = maxAlignment!(U);} // Each internal operation is encoded with an identifier. See// the "handler" function below.private enum OpID { getTypeInfo, get, compare, equals, testConversion, toString, index, indexAssign, catAssign, copyOut, length, apply, postblit, destruct } /** * Back-end type seldom used directly by user * code. Two commonly-used types using `VariantN` are: * * $(OL $(LI $(LREF Algebraic): A closed discriminated union with a * limited type universe (e.g., $(D Algebraic!(int, double, * string)) only accepts these three types and rejects anything * else).) $(LI $(LREF Variant): An open discriminated union allowing an * unbounded set of types. If any of the types in the `Variant` * are larger than the largest built-in type, they will automatically * be boxed. This means that even large types will only be the size * of a pointer within the `Variant`, but this also implies some * overhead. `Variant` can accommodate all primitive types and * all user-defined types.)) * * Both `Algebraic` and `Variant` share $(D * VariantN)'s interface. (See their respective documentations below.) * * `VariantN` is a discriminated union type parameterized * with the largest size of the types stored (`maxDataSize`) * and with the list of allowed types (`AllowedTypes`). If * the list is empty, then any type up of size up to $(D * maxDataSize) (rounded up for alignment) can be stored in a * `VariantN` object without being boxed (types larger * than this will be boxed). * */struct VariantN(size_t maxDataSize, AllowedTypesParam...){ /** The list of allowed types. If empty, any type is allowed. */ alias AllowedTypes = This2Variant!(VariantN, AllowedTypesParam); private: // Compute the largest practical size from maxDataSize struct SizeChecker { int function() fptr; ubyte[maxDataSize] data; } enum size = SizeChecker.sizeof - (int function()).sizeof; /** Tells whether a type `T` is statically _allowed for * storage inside a `VariantN` object by looking * `T` up in `AllowedTypes`. */ public template allowed(T) { enum bool allowed = is(T == VariantN) || //T.sizeof <= size && (AllowedTypes.length == 0 || staticIndexOf!(T, AllowedTypes) >= 0); } enum biggerThanSize(alias T) = T.sizeof > size; enum needsPostblit = !AllowedTypes.length || anySatisfy!(hasElaborateCopyConstructor, AllowedTypes) // if bigger than size, its stored by ref and needs to be copied || anySatisfy!(biggerThanSize, AllowedTypes); // state union { align(maxVariantAlignment!(AllowedTypes)) ubyte[size] store; // conservatively mark the region as pointers static if (size >= (void*).sizeof) void*[size / (void*).sizeof] p; } ptrdiff_t function(OpID selector, ubyte[size]* store, void* data) fptr = &handler!(void); // internals // Handler for an uninitialized value static ptrdiff_t handler(A : void)(OpID selector, ubyte[size]*, void* parm) { switch (selector) { case OpID.getTypeInfo: *cast(TypeInfo *) parm = typeid(A); break; case OpID.copyOut: auto target = cast(VariantN *) parm; target.fptr = &handler!(A); // no need to copy the data (it's garbage) break; case OpID.compare: case OpID.equals: auto rhs = cast(const VariantN *) parm; return rhs.peek!(A) ? 0 // all uninitialized are equal : ptrdiff_t.min; // uninitialized variant is not comparable otherwise case OpID.toString: string * target = cast(string*) parm; *target = "<Uninitialized VariantN>"; break; case OpID.postblit: case OpID.destruct: break; case OpID.get: case OpID.testConversion: case OpID.index: case OpID.indexAssign: case OpID.catAssign: case OpID.length: throw new VariantException( "Attempt to use an uninitialized VariantN"); default: assert(false, "Invalid OpID"); } return 0; } // Handler for all of a type's operations static ptrdiff_t handler(A)(OpID selector, ubyte[size]* pStore, void* parm) { import std.conv : to; static A* getPtr(void* untyped) { if (untyped) { static if (A.sizeof <= size) return cast(A*) untyped; else return *cast(A**) untyped; } return null; } static ptrdiff_t compare(A* rhsPA, A* zis, OpID selector) { static if (is(typeof(*rhsPA == *zis))) { enum isEmptyStructWithoutOpEquals = is(A == struct) && A.tupleof.length == 0 && !__traits(hasMember, A, "opEquals"); static if (isEmptyStructWithoutOpEquals) { // The check above will always succeed if A is an empty struct. // Don't generate unreachable code as seen in // https://issues.dlang.org/show_bug.cgi?id=21231 return 0; } else { if (*rhsPA == *zis) return 0; static if (is(typeof(*zis < *rhsPA))) { // Many types (such as any using the default Object opCmp) // will throw on an invalid opCmp, so do it only // if the caller requests it. if (selector == OpID.compare) return *zis < *rhsPA ? -1 : 1; else return ptrdiff_t.min; } else { // Not equal, and type does not support ordering // comparisons. return ptrdiff_t.min; } } } else { // Type does not support comparisons at all. return ptrdiff_t.min; } } auto zis = getPtr(pStore); // Input: TypeInfo object // Output: target points to a copy of *me, if me was not null // Returns: true iff the A can be converted to the type represented // by the incoming TypeInfo static bool tryPutting(A* src, TypeInfo targetType, void* target) { alias UA = Unqual!A; static if (isStaticArray!A && is(typeof(UA.init[0]))) { alias MutaTypes = AliasSeq!(UA, typeof(UA.init[0])[], AllImplicitConversionTargets!UA); } else { alias MutaTypes = AliasSeq!(UA, AllImplicitConversionTargets!UA); } alias ConstTypes = staticMap!(ConstOf, MutaTypes); alias SharedTypes = staticMap!(SharedOf, MutaTypes); alias SharedConstTypes = staticMap!(SharedConstOf, MutaTypes); alias ImmuTypes = staticMap!(ImmutableOf, MutaTypes); static if (is(A == immutable)) alias AllTypes = AliasSeq!(ImmuTypes, ConstTypes, SharedConstTypes); else static if (is(A == shared)) { static if (is(A == const)) alias AllTypes = SharedConstTypes; else alias AllTypes = AliasSeq!(SharedTypes, SharedConstTypes); } else { static if (is(A == const)) alias AllTypes = ConstTypes; else alias AllTypes = AliasSeq!(MutaTypes, ConstTypes); } foreach (T ; AllTypes) { if (targetType != typeid(T)) continue; // SPECIAL NOTE: variant only will ever create a new value with // tryPutting (effectively), and T is ALWAYS the same type of // A, but with different modifiers (and a limited set of // implicit targets). So this checks to see if we can construct // a T from A, knowing that prerequisite. This handles issues // where the type contains some constant data aside from the // modifiers on the type itself. static if (is(typeof(delegate T() {return *src;})) || is(T == const(U), U) || is(T == shared(U), U) || is(T == shared const(U), U) || is(T == immutable(U), U)) { import core.internal.lifetime : emplaceRef; auto zat = cast(T*) target; if (src) { static if (T.sizeof > 0) assert(target, "target must be non-null"); static if (isStaticArray!A && isDynamicArray!T) { auto this_ = (*src)[]; emplaceRef(*cast(Unqual!T*) zat, cast() cast(T) this_); } else { emplaceRef(*cast(Unqual!T*) zat, *cast(UA*) src); } } } else { // type T is not constructible from A if (src) assert(false, A.stringof); } return true; } return false; } switch (selector) { case OpID.getTypeInfo: *cast(TypeInfo *) parm = typeid(A); break; case OpID.copyOut: auto target = cast(VariantN *) parm; assert(target); static if (target.size < A.sizeof) { if (target.type.tsize < A.sizeof) { static if (is(A == U[n], U, size_t n)) { A* p = cast(A*)(new U[n]).ptr; } else { // object will be intialized later. Using malloc in case `this()` is disabled A* p = cast(A*) GC.malloc(A.sizeof, 0, typeid(A)); } *cast(A**)&target.store = p; } } tryPutting(zis, typeid(A), cast(void*) getPtr(&target.store)) || assert(false); target.fptr = &handler!(A); break; case OpID.get: auto t = * cast(Tuple!(TypeInfo, void*)*) parm; return !tryPutting(zis, t[0], t[1]); case OpID.testConversion: return !tryPutting(null, *cast(TypeInfo*) parm, null); case OpID.compare: case OpID.equals: auto rhsP = cast(VariantN *) parm; auto rhsType = rhsP.type; // Are we the same? if (rhsType == typeid(A)) { // cool! Same type! auto rhsPA = getPtr(&rhsP.store); return compare(rhsPA, zis, selector); } else if (rhsType == typeid(void)) { // No support for ordering comparisons with // uninitialized vars return ptrdiff_t.min; } VariantN temp; // Do I convert to rhs? if (tryPutting(zis, rhsType, &temp.store)) { // cool, I do; temp's store contains my data in rhs's type! // also fix up its fptr temp.fptr = rhsP.fptr; // now lhsWithRhsType is a full-blown VariantN of rhs's type if (selector == OpID.compare) return temp.opCmp(*rhsP); else return temp.opEquals(*rhsP) ? 0 : 1; } // Does rhs convert to zis? auto t = tuple(typeid(A), &temp.store); if (rhsP.fptr(OpID.get, &rhsP.store, &t) == 0) { // cool! Now temp has rhs in my type! auto rhsPA = getPtr(&temp.store); return compare(rhsPA, zis, selector); } // Generate the function below only if the Variant's type is // comparable with 'null' static if (__traits(compiles, () => A.init == null)) { if (rhsType == typeid(null)) { // if rhsType is typeof(null), then we're comparing with 'null' // this takes into account 'opEquals' and 'opCmp' // all types that can compare with null have to following properties: // if it's 'null' then it's equal to null, otherwise it's always greater // than 'null' return *zis == null ? 0 : 1; } } return ptrdiff_t.min; // dunno case OpID.toString:Lines 1–500 of 3475. Load more
// Written in the D programming language. /** This module implements a $(HTTP erdani.org/publications/cuj-04-2002.php.html,discriminated union) type (a.k.a. $(HTTP en.wikipedia.org/wiki/Tagged_union,tagged union), $(HTTP en.wikipedia.org/wiki/Algebraic_data_type,algebraic type)). Such types are useful for type-uniform binary interfaces, interfacing with scripting languages, and comfortable exploratory programming. A $(LREF Variant) object can hold a value of any type, with very few restrictions (such as `shared` types and noncopyable types). Setting the value is as immediate as assigning to the `Variant` object. To read back the value of the appropriate type `T`, use the $(LREF get) method. To query whether a `Variant` currently holds a value of type `T`, use $(LREF peek). To fetch the exact type currently held, call $(LREF type), which returns the `TypeInfo` of the current value. In addition to $(LREF Variant), this module also defines the $(LREF Algebraic) type constructor. Unlike `Variant`, `Algebraic` only allows a finite set of types, which are specified in the instantiation (e.g. $(D Algebraic!(int, string)) may only hold an `int` or a `string`). $(RED Warning: $(LREF Algebraic) is outdated and not recommended for use in new code. Instead, use $(REF SumType, std,sumtype).) Credits: Reviewed by Brad Roberts. Daniel Keep provided a detailed code review prompting the following improvements: (1) better support for arrays; (2) support for associative arrays; (3) friendlier behavior towards the garbage collector. Copyright: Copyright Andrei Alexandrescu 2007 - 2015. License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP erdani.org, Andrei Alexandrescu) Source: $(PHOBOSSRC std/variant.d) */ module std.variant; import std.meta, std.traits, std.typecons; import core.memory : GC; /// @system unittest { Variant a; // Must assign before use, otherwise exception ensues // Initialize with an integer; make the type int Variant b = 42; assert(b.type == typeid(int)); // Peek at the value assert(b.peek!(int) !is null && *b.peek!(int) == 42); // Automatically convert per language rules auto x = b.get!(real); // Assign any other type, including other variants a = b; a = 3.14; assert(a.type == typeid(double)); // Implicit conversions work just as with built-in types assert(a < b); // Check for convertibility assert(!a.convertsTo!(int)); // double not convertible to int // Strings and all other arrays are supported a = "now I'm a string"; assert(a == "now I'm a string"); // can also assign arrays a = new int[42]; assert(a.length == 42); a[5] = 7; assert(a[5] == 7); // Can also assign class values class Foo {} auto foo = new Foo; a = foo; assert(*a.peek!(Foo) == foo); // and full type information is preserved } /++ Gives the `sizeof` the largest type given. See_Also: $(LINK https://forum.dlang.org/thread/wbpnncxepehgcswhuazl@forum.dlang.org?page=1) +/ template maxSize(Ts...) { align(1) union Impl { static foreach (i, T; Ts) { static if (!is(T == void)) mixin("T _field_", i, ";"); } } enum maxSize = Impl.sizeof; } /// @safe unittest { struct Cat { int a, b, c; } align(1) struct S { long l; ubyte b; } align(1) struct T { ubyte b; long l; } static assert(maxSize!(int, long) == 8); static assert(maxSize!(bool, byte) == 1); static assert(maxSize!(bool, Cat) == 12); static assert(maxSize!(char) == 1); static assert(maxSize!(char, short, ubyte) == 2); static assert(maxSize!(char, long, ubyte) == 8); import std.algorithm.comparison : max; static assert(maxSize!(long, S) == max(long.sizeof, S.sizeof)); static assert(maxSize!(S, T) == max(S.sizeof, T.sizeof)); static assert(maxSize!(int, ubyte[7]) == 7); static assert(maxSize!(int, ubyte[3]) == 4); static assert(maxSize!(int, int, ubyte[3]) == 4); static assert(maxSize!(void, int, ubyte[3]) == 4); static assert(maxSize!(void) == 1); } struct This; private alias This2Variant(V, T...) = AliasSeq!(ReplaceTypeUnless!(isAlgebraic, This, V, T)); // We can't just use maxAlignment because no types might be specified // to VariantN, so handle that here and then pass along the rest. private template maxVariantAlignment(U...) if (isTypeTuple!U) { static if (U.length == 0) { import std.algorithm.comparison : max; enum maxVariantAlignment = max(real.alignof, size_t.alignof); } else enum maxVariantAlignment = maxAlignment!(U); } // Each internal operation is encoded with an identifier. See // the "handler" function below. private enum OpID { getTypeInfo, get, compare, equals, testConversion, toString, index, indexAssign, catAssign, copyOut, length, apply, postblit, destruct } /** * Back-end type seldom used directly by user * code. Two commonly-used types using `VariantN` are: * * $(OL $(LI $(LREF Algebraic): A closed discriminated union with a * limited type universe (e.g., $(D Algebraic!(int, double, * string)) only accepts these three types and rejects anything * else).) $(LI $(LREF Variant): An open discriminated union allowing an * unbounded set of types. If any of the types in the `Variant` * are larger than the largest built-in type, they will automatically * be boxed. This means that even large types will only be the size * of a pointer within the `Variant`, but this also implies some * overhead. `Variant` can accommodate all primitive types and * all user-defined types.)) * * Both `Algebraic` and `Variant` share $(D * VariantN)'s interface. (See their respective documentations below.) * * `VariantN` is a discriminated union type parameterized * with the largest size of the types stored (`maxDataSize`) * and with the list of allowed types (`AllowedTypes`). If * the list is empty, then any type up of size up to $(D * maxDataSize) (rounded up for alignment) can be stored in a * `VariantN` object without being boxed (types larger * than this will be boxed). * */ struct VariantN(size_t maxDataSize, AllowedTypesParam...) { /** The list of allowed types. If empty, any type is allowed. */ alias AllowedTypes = This2Variant!(VariantN, AllowedTypesParam); private: // Compute the largest practical size from maxDataSize struct SizeChecker { int function() fptr; ubyte[maxDataSize] data; } enum size = SizeChecker.sizeof - (int function()).sizeof; /** Tells whether a type `T` is statically _allowed for * storage inside a `VariantN` object by looking * `T` up in `AllowedTypes`. */ public template allowed(T) { enum bool allowed = is(T == VariantN) || //T.sizeof <= size && (AllowedTypes.length == 0 || staticIndexOf!(T, AllowedTypes) >= 0); } enum biggerThanSize(alias T) = T.sizeof > size; enum needsPostblit = !AllowedTypes.length || anySatisfy!(hasElaborateCopyConstructor, AllowedTypes) // if bigger than size, its stored by ref and needs to be copied || anySatisfy!(biggerThanSize, AllowedTypes); // state union { align(maxVariantAlignment!(AllowedTypes)) ubyte[size] store; // conservatively mark the region as pointers static if (size >= (void*).sizeof) void*[size / (void*).sizeof] p; } ptrdiff_t function(OpID selector, ubyte[size]* store, void* data) fptr = &handler!(void); // internals // Handler for an uninitialized value static ptrdiff_t handler(A : void)(OpID selector, ubyte[size]*, void* parm) { switch (selector) { case OpID.getTypeInfo: *cast(TypeInfo *) parm = typeid(A); break; case OpID.copyOut: auto target = cast(VariantN *) parm; target.fptr = &handler!(A); // no need to copy the data (it's garbage) break; case OpID.compare: case OpID.equals: auto rhs = cast(const VariantN *) parm; return rhs.peek!(A) ? 0 // all uninitialized are equal : ptrdiff_t.min; // uninitialized variant is not comparable otherwise case OpID.toString: string * target = cast(string*) parm; *target = "<Uninitialized VariantN>"; break; case OpID.postblit: case OpID.destruct: break; case OpID.get: case OpID.testConversion: case OpID.index: case OpID.indexAssign: case OpID.catAssign: case OpID.length: throw new VariantException( "Attempt to use an uninitialized VariantN"); default: assert(false, "Invalid OpID"); } return 0; } // Handler for all of a type's operations static ptrdiff_t handler(A)(OpID selector, ubyte[size]* pStore, void* parm) { import std.conv : to; static A* getPtr(void* untyped) { if (untyped) { static if (A.sizeof <= size) return cast(A*) untyped; else return *cast(A**) untyped; } return null; } static ptrdiff_t compare(A* rhsPA, A* zis, OpID selector) { static if (is(typeof(*rhsPA == *zis))) { enum isEmptyStructWithoutOpEquals = is(A == struct) && A.tupleof.length == 0 && !__traits(hasMember, A, "opEquals"); static if (isEmptyStructWithoutOpEquals) { // The check above will always succeed if A is an empty struct. // Don't generate unreachable code as seen in // https://issues.dlang.org/show_bug.cgi?id=21231 return 0; } else { if (*rhsPA == *zis) return 0; static if (is(typeof(*zis < *rhsPA))) { // Many types (such as any using the default Object opCmp) // will throw on an invalid opCmp, so do it only // if the caller requests it. if (selector == OpID.compare) return *zis < *rhsPA ? -1 : 1; else return ptrdiff_t.min; } else { // Not equal, and type does not support ordering // comparisons. return ptrdiff_t.min; } } } else { // Type does not support comparisons at all. return ptrdiff_t.min; } } auto zis = getPtr(pStore); // Input: TypeInfo object // Output: target points to a copy of *me, if me was not null // Returns: true iff the A can be converted to the type represented // by the incoming TypeInfo static bool tryPutting(A* src, TypeInfo targetType, void* target) { alias UA = Unqual!A; static if (isStaticArray!A && is(typeof(UA.init[0]))) { alias MutaTypes = AliasSeq!(UA, typeof(UA.init[0])[], AllImplicitConversionTargets!UA); } else { alias MutaTypes = AliasSeq!(UA, AllImplicitConversionTargets!UA); } alias ConstTypes = staticMap!(ConstOf, MutaTypes); alias SharedTypes = staticMap!(SharedOf, MutaTypes); alias SharedConstTypes = staticMap!(SharedConstOf, MutaTypes); alias ImmuTypes = staticMap!(ImmutableOf, MutaTypes); static if (is(A == immutable)) alias AllTypes = AliasSeq!(ImmuTypes, ConstTypes, SharedConstTypes); else static if (is(A == shared)) { static if (is(A == const)) alias AllTypes = SharedConstTypes; else alias AllTypes = AliasSeq!(SharedTypes, SharedConstTypes); } else { static if (is(A == const)) alias AllTypes = ConstTypes; else alias AllTypes = AliasSeq!(MutaTypes, ConstTypes); } foreach (T ; AllTypes) { if (targetType != typeid(T)) continue; // SPECIAL NOTE: variant only will ever create a new value with // tryPutting (effectively), and T is ALWAYS the same type of // A, but with different modifiers (and a limited set of // implicit targets). So this checks to see if we can construct // a T from A, knowing that prerequisite. This handles issues // where the type contains some constant data aside from the // modifiers on the type itself. static if (is(typeof(delegate T() {return *src;})) || is(T == const(U), U) || is(T == shared(U), U) || is(T == shared const(U), U) || is(T == immutable(U), U)) { import core.internal.lifetime : emplaceRef; auto zat = cast(T*) target; if (src) { static if (T.sizeof > 0) assert(target, "target must be non-null"); static if (isStaticArray!A && isDynamicArray!T) { auto this_ = (*src)[]; emplaceRef(*cast(Unqual!T*) zat, cast() cast(T) this_); } else { emplaceRef(*cast(Unqual!T*) zat, *cast(UA*) src); } } } else { // type T is not constructible from A if (src) assert(false, A.stringof); } return true; } return false; } switch (selector) { case OpID.getTypeInfo: *cast(TypeInfo *) parm = typeid(A); break; case OpID.copyOut: auto target = cast(VariantN *) parm; assert(target); static if (target.size < A.sizeof) { if (target.type.tsize < A.sizeof) { static if (is(A == U[n], U, size_t n)) { A* p = cast(A*)(new U[n]).ptr; } else { // object will be intialized later. Using malloc in case `this()` is disabled A* p = cast(A*) GC.malloc(A.sizeof, 0, typeid(A)); } *cast(A**)&target.store = p; } } tryPutting(zis, typeid(A), cast(void*) getPtr(&target.store)) || assert(false); target.fptr = &handler!(A); break; case OpID.get: auto t = * cast(Tuple!(TypeInfo, void*)*) parm; return !tryPutting(zis, t[0], t[1]); case OpID.testConversion: return !tryPutting(null, *cast(TypeInfo*) parm, null); case OpID.compare: case OpID.equals: auto rhsP = cast(VariantN *) parm; auto rhsType = rhsP.type; // Are we the same? if (rhsType == typeid(A)) { // cool! Same type! auto rhsPA = getPtr(&rhsP.store); return compare(rhsPA, zis, selector); } else if (rhsType == typeid(void)) { // No support for ordering comparisons with // uninitialized vars return ptrdiff_t.min; } VariantN temp; // Do I convert to rhs? if (tryPutting(zis, rhsType, &temp.store)) { // cool, I do; temp's store contains my data in rhs's type! // also fix up its fptr temp.fptr = rhsP.fptr; // now lhsWithRhsType is a full-blown VariantN of rhs's type if (selector == OpID.compare) return temp.opCmp(*rhsP); else return temp.opEquals(*rhsP) ? 0 : 1; } // Does rhs convert to zis? auto t = tuple(typeid(A), &temp.store); if (rhsP.fptr(OpID.get, &rhsP.store, &t) == 0) { // cool! Now temp has rhs in my type! auto rhsPA = getPtr(&temp.store); return compare(rhsPA, zis, selector); } // Generate the function below only if the Variant's type is // comparable with 'null' static if (__traits(compiles, () => A.init == null)) { if (rhsType == typeid(null)) { // if rhsType is typeof(null), then we're comparing with 'null' // this takes into account 'opEquals' and 'opCmp' // all types that can compare with null have to following properties: // if it's 'null' then it's equal to null, otherwise it's always greater // than 'null' return *zis == null ? 0 : 1; } } return ptrdiff_t.min; // dunno case OpID.toString: