|  | // 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; | 
|  |  | 
|  | /// | 
|  | @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); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * 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); | 
|  | } | 
|  |  | 
|  | // Each internal operation is encoded with an identifier. See | 
|  | // the "handler" function below. | 
|  | enum OpID { getTypeInfo, get, compare, equals, testConversion, toString, | 
|  | index, indexAssign, catAssign, copyOut, length, | 
|  | apply, postblit, destruct } | 
|  |  | 
|  | // 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(Unqual!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 | 
|  | { | 
|  | A* p = new 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: | 
|  | auto target = cast(string*) parm; | 
|  | static if (is(typeof(to!(string)(*zis)))) | 
|  | { | 
|  | *target = to!(string)(*zis); | 
|  | break; | 
|  | } | 
|  | // TODO: The following test evaluates to true for shared objects. | 
|  | //       Use __traits for now until this is sorted out. | 
|  | // else static if (is(typeof((*zis).toString))) | 
|  | else static if (__traits(compiles, {(*zis).toString();})) | 
|  | { | 
|  | *target = (*zis).toString(); | 
|  | break; | 
|  | } | 
|  | else | 
|  | { | 
|  | throw new VariantException(typeid(A), typeid(string)); | 
|  | } | 
|  |  | 
|  | case OpID.index: | 
|  | auto result = cast(Variant*) parm; | 
|  | static if (isArray!(A) && !is(immutable typeof(A.init[0]) == immutable void)) | 
|  | { | 
|  | // array type; input and output are the same VariantN | 
|  | size_t index = result.convertsTo!(int) | 
|  | ? result.get!(int) : result.get!(size_t); | 
|  | *result = (*zis)[index]; | 
|  | break; | 
|  | } | 
|  | else static if (isAssociativeArray!(A)) | 
|  | { | 
|  | *result = (*zis)[result.get!(typeof(A.init.keys[0]))]; | 
|  | break; | 
|  | } | 
|  | else | 
|  | { | 
|  | throw new VariantException(typeid(A), result[0].type); | 
|  | } | 
|  |  | 
|  | case OpID.indexAssign: | 
|  | // array type; result comes first, index comes second | 
|  | auto args = cast(Variant*) parm; | 
|  | static if (isArray!(A) && is(typeof((*zis)[0] = (*zis)[0]))) | 
|  | { | 
|  | size_t index = args[1].convertsTo!(int) | 
|  | ? args[1].get!(int) : args[1].get!(size_t); | 
|  | (*zis)[index] = args[0].get!(typeof((*zis)[0])); | 
|  | break; | 
|  | } | 
|  | else static if (isAssociativeArray!(A) && is(typeof((*zis)[A.init.keys[0]] = A.init.values[0]))) | 
|  | { | 
|  | (*zis)[args[1].get!(typeof(A.init.keys[0]))] | 
|  | = args[0].get!(typeof(A.init.values[0])); | 
|  | break; | 
|  | } | 
|  | else | 
|  | { | 
|  | throw new VariantException(typeid(A), args[0].type); | 
|  | } | 
|  |  | 
|  | case OpID.catAssign: | 
|  | static if (!is(immutable typeof((*zis)[0]) == immutable void) && | 
|  | is(typeof((*zis)[0])) && is(typeof(*zis ~= *zis))) | 
|  | { | 
|  | // array type; parm is the element to append | 
|  | auto arg = cast(Variant*) parm; | 
|  | alias E = typeof((*zis)[0]); | 
|  | if (arg[0].convertsTo!(E)) | 
|  | { | 
|  | // append one element to the array | 
|  | (*zis) ~= [ arg[0].get!(E) ]; | 
|  | } | 
|  | else | 
|  | { | 
|  | // append a whole array to the array | 
|  | (*zis) ~= arg[0].get!(A); | 
|  | } | 
|  | break; | 
|  | } | 
|  | else | 
|  | { | 
|  | throw new VariantException(typeid(A), typeid(void[])); | 
|  | } | 
|  |  | 
|  | case OpID.length: | 
|  | static if (isArray!(A) || isAssociativeArray!(A)) | 
|  | { | 
|  | return zis.length; | 
|  | } | 
|  | else | 
|  | { | 
|  | throw new VariantException(typeid(A), typeid(void[])); | 
|  | } | 
|  |  | 
|  | case OpID.apply: | 
|  | static if (!isFunctionPointer!A && !isDelegate!A) | 
|  | { | 
|  | import std.conv : text; | 
|  | import std.exception : enforce; | 
|  | enforce(0, text("Cannot apply `()' to a value of type `", | 
|  | A.stringof, "'.")); | 
|  | } | 
|  | else | 
|  | { | 
|  | import std.conv : text; | 
|  | import std.exception : enforce; | 
|  | alias ParamTypes = Parameters!A; | 
|  | auto p = cast(Variant*) parm; | 
|  | auto argCount = p.get!size_t; | 
|  | // To assign the tuple we need to use the unqualified version, | 
|  | // otherwise we run into issues such as with const values. | 
|  | // We still get the actual type from the Variant though | 
|  | // to ensure that we retain const correctness. | 
|  | Tuple!(staticMap!(Unqual, ParamTypes)) t; | 
|  | enforce(t.length == argCount, | 
|  | text("Argument count mismatch: ", | 
|  | A.stringof, " expects ", t.length, | 
|  | " argument(s), not ", argCount, ".")); | 
|  | auto variantArgs = p[1 .. argCount + 1]; | 
|  | foreach (i, T; ParamTypes) | 
|  | { | 
|  | t[i] = cast() variantArgs[i].get!T; | 
|  | } | 
|  |  | 
|  | auto args = cast(Tuple!(ParamTypes))t; | 
|  | static if (is(ReturnType!A == void)) | 
|  | { | 
|  | (*zis)(args.expand); | 
|  | *p = Variant.init; // void returns uninitialized Variant. | 
|  | } | 
|  | else | 
|  | { | 
|  | *p = (*zis)(args.expand); | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case OpID.postblit: | 
|  | static if (hasElaborateCopyConstructor!A) | 
|  | { | 
|  | zis.__xpostblit(); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case OpID.destruct: | 
|  | static if (hasElaborateDestructor!A) | 
|  | { | 
|  | zis.__xdtor(); | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: assert(false); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | public: | 
|  | /** Constructs a `VariantN` value given an argument of a | 
|  | * generic type. Statically rejects disallowed types. | 
|  | */ | 
|  |  | 
|  | this(T)(T value) | 
|  | { | 
|  | static assert(allowed!(T), "Cannot store a " ~ T.stringof | 
|  | ~ " in a " ~ VariantN.stringof); | 
|  | opAssign(value); | 
|  | } | 
|  |  | 
|  | /// Allows assignment from a subset algebraic type | 
|  | this(T : VariantN!(tsize, Types), size_t tsize, Types...)(T value) | 
|  | if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types)) | 
|  | { | 
|  | opAssign(value); | 
|  | } | 
|  |  | 
|  | static if (!AllowedTypes.length || anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) | 
|  | { | 
|  | this(this) | 
|  | { | 
|  | fptr(OpID.postblit, &store, null); | 
|  | } | 
|  | } | 
|  |  | 
|  | static if (!AllowedTypes.length || anySatisfy!(hasElaborateDestructor, AllowedTypes)) | 
|  | { | 
|  | ~this() | 
|  | { | 
|  | // Infer the safety of the provided types | 
|  | static if (AllowedTypes.length) | 
|  | { | 
|  | if (0) | 
|  | { | 
|  | AllowedTypes var; | 
|  | } | 
|  | } | 
|  | (() @trusted => fptr(OpID.destruct, &store, null))(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Assigns a `VariantN` from a generic | 
|  | * argument. Statically rejects disallowed types. */ | 
|  |  | 
|  | VariantN opAssign(T)(T rhs) | 
|  | { | 
|  | static assert(allowed!(T), "Cannot store a " ~ T.stringof | 
|  | ~ " in a " ~ VariantN.stringof ~ ". Valid types are " | 
|  | ~ AllowedTypes.stringof); | 
|  |  | 
|  | static if (is(T : VariantN)) | 
|  | { | 
|  | rhs.fptr(OpID.copyOut, &rhs.store, &this); | 
|  | } | 
|  | else static if (is(T : const(VariantN))) | 
|  | { | 
|  | static assert(false, | 
|  | "Assigning Variant objects from const Variant"~ | 
|  | " objects is currently not supported."); | 
|  | } | 
|  | else | 
|  | { | 
|  | import core.lifetime : copyEmplace; | 
|  |  | 
|  | static if (!AllowedTypes.length || anySatisfy!(hasElaborateDestructor, AllowedTypes)) | 
|  | { | 
|  | // Assignment should destruct previous value | 
|  | fptr(OpID.destruct, &store, null); | 
|  | } | 
|  |  | 
|  | static if (T.sizeof <= size) | 
|  | copyEmplace(rhs, *cast(T*) &store); | 
|  | else | 
|  | { | 
|  | static if (is(T == U[n], U, size_t n)) | 
|  | auto p = cast(T*) (new U[n]).ptr; | 
|  | else | 
|  | auto p = new T; | 
|  | copyEmplace(rhs, *p); | 
|  | *(cast(T**) &store) = p; | 
|  | } | 
|  |  | 
|  | fptr = &handler!(T); | 
|  | } | 
|  | return this; | 
|  | } | 
|  |  | 
|  | // Allow assignment from another variant which is a subset of this one | 
|  | VariantN opAssign(T : VariantN!(tsize, Types), size_t tsize, Types...)(T rhs) | 
|  | if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types)) | 
|  | { | 
|  | // discover which type rhs is actually storing | 
|  | foreach (V; T.AllowedTypes) | 
|  | if (rhs.type == typeid(V)) | 
|  | return this = rhs.get!V; | 
|  | assert(0, T.AllowedTypes.stringof); | 
|  | } | 
|  |  | 
|  |  | 
|  | Variant opCall(P...)(auto ref P params) | 
|  | { | 
|  | Variant[P.length + 1] pack; | 
|  | pack[0] = P.length; | 
|  | foreach (i, _; params) | 
|  | { | 
|  | pack[i + 1] = params[i]; | 
|  | } | 
|  | fptr(OpID.apply, &store, &pack); | 
|  | return pack[0]; | 
|  | } | 
|  |  | 
|  | /** Returns true if and only if the `VariantN` object | 
|  | * holds a valid value (has been initialized with, or assigned | 
|  | * from, a valid value). | 
|  | */ | 
|  | @property bool hasValue() const pure nothrow | 
|  | { | 
|  | // @@@BUG@@@ in compiler, the cast shouldn't be needed | 
|  | return cast(typeof(&handler!(void))) fptr != &handler!(void); | 
|  | } | 
|  |  | 
|  | /// | 
|  | version (StdDdoc) | 
|  | @system unittest | 
|  | { | 
|  | Variant a; | 
|  | assert(!a.hasValue); | 
|  | Variant b; | 
|  | a = b; | 
|  | assert(!a.hasValue); // still no value | 
|  | a = 5; | 
|  | assert(a.hasValue); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * If the `VariantN` object holds a value of the | 
|  | * $(I exact) type `T`, returns a pointer to that | 
|  | * value. Otherwise, returns `null`. In cases | 
|  | * where `T` is statically disallowed, $(D | 
|  | * peek) will not compile. | 
|  | */ | 
|  | @property inout(T)* peek(T)() inout | 
|  | { | 
|  | static if (!is(T == void)) | 
|  | static assert(allowed!(T), "Cannot store a " ~ T.stringof | 
|  | ~ " in a " ~ VariantN.stringof); | 
|  | if (type != typeid(T)) | 
|  | return null; | 
|  | static if (T.sizeof <= size) | 
|  | return cast(inout T*)&store; | 
|  | else | 
|  | return *cast(inout T**)&store; | 
|  | } | 
|  |  | 
|  | /// | 
|  | version (StdDdoc) | 
|  | @system unittest | 
|  | { | 
|  | Variant a = 5; | 
|  | auto b = a.peek!(int); | 
|  | assert(b !is null); | 
|  | *b = 6; | 
|  | assert(a == 6); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the `typeid` of the currently held value. | 
|  | */ | 
|  |  | 
|  | @property TypeInfo type() const nothrow @trusted | 
|  | { | 
|  | scope(failure) assert(0); | 
|  |  | 
|  | TypeInfo result; | 
|  | fptr(OpID.getTypeInfo, null, &result); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns `true` if and only if the `VariantN` | 
|  | * object holds an object implicitly convertible to type `T`. | 
|  | * Implicit convertibility is defined as per | 
|  | * $(REF_ALTTEXT AllImplicitConversionTargets, AllImplicitConversionTargets, std,traits). | 
|  | */ | 
|  |  | 
|  | @property bool convertsTo(T)() const | 
|  | { | 
|  | TypeInfo info = typeid(T); | 
|  | return fptr(OpID.testConversion, null, &info) == 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | Returns the value stored in the `VariantN` object, either by specifying the | 
|  | needed type or the index in the list of allowed types. The latter overload | 
|  | only applies to bounded variants (e.g. $(LREF Algebraic)). | 
|  |  | 
|  | Params: | 
|  | T = The requested type. The currently stored value must implicitly convert | 
|  | to the requested type, in fact `DecayStaticToDynamicArray!T`. If an | 
|  | implicit conversion is not possible, throws a `VariantException`. | 
|  | index = The index of the type among `AllowedTypesParam`, zero-based. | 
|  | */ | 
|  | @property inout(T) get(T)() inout | 
|  | { | 
|  | inout(T) result = void; | 
|  | static if (is(T == shared)) | 
|  | alias R = shared Unqual!T; | 
|  | else | 
|  | alias R = Unqual!T; | 
|  | auto buf = tuple(typeid(T), cast(R*)&result); | 
|  |  | 
|  | if (fptr(OpID.get, cast(ubyte[size]*) &store, &buf)) | 
|  | { | 
|  | throw new VariantException(type, typeid(T)); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /// Ditto | 
|  | @property auto get(uint index)() inout | 
|  | if (index < AllowedTypes.length) | 
|  | { | 
|  | foreach (i, T; AllowedTypes) | 
|  | { | 
|  | static if (index == i) return get!T; | 
|  | } | 
|  | assert(0); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the value stored in the `VariantN` object, | 
|  | * explicitly converted (coerced) to the requested type $(D | 
|  | * T). If `T` is a string type, the value is formatted as | 
|  | * a string. If the `VariantN` object is a string, a | 
|  | * parse of the string to type `T` is attempted. If a | 
|  | * conversion is not possible, throws a $(D | 
|  | * VariantException). | 
|  | */ | 
|  |  | 
|  | @property T coerce(T)() | 
|  | { | 
|  | import std.conv : to, text; | 
|  | static if (isNumeric!T || isBoolean!T) | 
|  | { | 
|  | if (convertsTo!real) | 
|  | { | 
|  | // maybe optimize this fella; handle ints separately | 
|  | return to!T(get!real); | 
|  | } | 
|  | else if (convertsTo!(const(char)[])) | 
|  | { | 
|  | return to!T(get!(const(char)[])); | 
|  | } | 
|  | // I'm not sure why this doesn't convert to const(char), | 
|  | // but apparently it doesn't (probably a deeper bug). | 
|  | // | 
|  | // Until that is fixed, this quick addition keeps a common | 
|  | // function working. "10".coerce!int ought to work. | 
|  | else if (convertsTo!(immutable(char)[])) | 
|  | { | 
|  | return to!T(get!(immutable(char)[])); | 
|  | } | 
|  | else | 
|  | { | 
|  | import std.exception : enforce; | 
|  | enforce(false, text("Type ", type, " does not convert to ", | 
|  | typeid(T))); | 
|  | assert(0); | 
|  | } | 
|  | } | 
|  | else static if (is(T : Object)) | 
|  | { | 
|  | return to!(T)(get!(Object)); | 
|  | } | 
|  | else static if (isSomeString!(T)) | 
|  | { | 
|  | return to!(T)(toString()); | 
|  | } | 
|  | else | 
|  | { | 
|  | // Fix for bug 1649 | 
|  | static assert(false, "unsupported type for coercion"); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Formats the stored value as a string. | 
|  | */ | 
|  |  | 
|  | string toString() | 
|  | { | 
|  | string result; | 
|  | fptr(OpID.toString, &store, &result) == 0 || assert(false); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Comparison for equality used by the "==" and "!="  operators. | 
|  | */ | 
|  |  | 
|  | // returns 1 if the two are equal | 
|  | bool opEquals(T)(auto ref T rhs) const | 
|  | if (allowed!T || is(immutable T == immutable VariantN)) | 
|  | { | 
|  | static if (is(immutable T == immutable VariantN)) | 
|  | alias temp = rhs; | 
|  | else | 
|  | auto temp = VariantN(rhs); | 
|  | return !fptr(OpID.equals, cast(ubyte[size]*) &store, | 
|  | cast(void*) &temp); | 
|  | } | 
|  |  | 
|  | // workaround for bug 10567 fix | 
|  | int opCmp(ref const VariantN rhs) const | 
|  | { | 
|  | return (cast() this).opCmp!(VariantN)(cast() rhs); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Ordering comparison used by the "<", "<=", ">", and ">=" | 
|  | * operators. In case comparison is not sensible between the held | 
|  | * value and `rhs`, an exception is thrown. | 
|  | */ | 
|  |  | 
|  | int opCmp(T)(T rhs) | 
|  | if (allowed!T)  // includes T == VariantN | 
|  | { | 
|  | static if (is(T == VariantN)) | 
|  | alias temp = rhs; | 
|  | else | 
|  | auto temp = VariantN(rhs); | 
|  | auto result = fptr(OpID.compare, &store, &temp); | 
|  | if (result == ptrdiff_t.min) | 
|  | { | 
|  | throw new VariantException(type, temp.type); | 
|  | } | 
|  |  | 
|  | assert(result >= -1 && result <= 1);  // Should be true for opCmp. | 
|  | return cast(int) result; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Computes the hash of the held value. | 
|  | */ | 
|  |  | 
|  | size_t toHash() const nothrow @safe | 
|  | { | 
|  | return type.getHash(&store); | 
|  | } | 
|  |  | 
|  | private VariantN opArithmetic(T, string op)(T other) | 
|  | { | 
|  | static if (isInstanceOf!(.VariantN, T)) | 
|  | { | 
|  | string tryUseType(string tp) | 
|  | { | 
|  | import std.format : format; | 
|  | return q{ | 
|  | static if (allowed!%1$s && T.allowed!%1$s) | 
|  | if (convertsTo!%1$s && other.convertsTo!%1$s) | 
|  | return VariantN(get!%1$s %2$s other.get!%1$s); | 
|  | }.format(tp, op); | 
|  | } | 
|  |  | 
|  | mixin(tryUseType("uint")); | 
|  | mixin(tryUseType("int")); | 
|  | mixin(tryUseType("ulong")); | 
|  | mixin(tryUseType("long")); | 
|  | mixin(tryUseType("float")); | 
|  | mixin(tryUseType("double")); | 
|  | mixin(tryUseType("real")); | 
|  | } | 
|  | else | 
|  | { | 
|  | static if (allowed!T) | 
|  | if (auto pv = peek!T) return VariantN(mixin("*pv " ~ op ~ " other")); | 
|  | static if (allowed!uint && is(typeof(T.max) : uint) && isUnsigned!T) | 
|  | if (convertsTo!uint) return VariantN(mixin("get!(uint) " ~ op ~ " other")); | 
|  | static if (allowed!int && is(typeof(T.max) : int) && !isUnsigned!T) | 
|  | if (convertsTo!int) return VariantN(mixin("get!(int) " ~ op ~ " other")); | 
|  | static if (allowed!ulong && is(typeof(T.max) : ulong) && isUnsigned!T) | 
|  | if (convertsTo!ulong) return VariantN(mixin("get!(ulong) " ~ op ~ " other")); | 
|  | static if (allowed!long && is(typeof(T.max) : long) && !isUnsigned!T) | 
|  | if (convertsTo!long) return VariantN(mixin("get!(long) " ~ op ~ " other")); | 
|  | static if (allowed!float && is(T : float)) | 
|  | if (convertsTo!float) return VariantN(mixin("get!(float) " ~ op ~ " other")); | 
|  | static if (allowed!double && is(T : double)) | 
|  | if (convertsTo!double) return VariantN(mixin("get!(double) " ~ op ~ " other")); | 
|  | static if (allowed!real && is (T : real)) | 
|  | if (convertsTo!real) return VariantN(mixin("get!(real) " ~ op ~ " other")); | 
|  | } | 
|  |  | 
|  | throw new VariantException("No possible match found for VariantN "~op~" "~T.stringof); | 
|  | } | 
|  |  | 
|  | private VariantN opLogic(T, string op)(T other) | 
|  | { | 
|  | VariantN result; | 
|  | static if (is(T == VariantN)) | 
|  | { | 
|  | if (convertsTo!(uint) && other.convertsTo!(uint)) | 
|  | result = mixin("get!(uint) " ~ op ~ " other.get!(uint)"); | 
|  | else if (convertsTo!(int) && other.convertsTo!(int)) | 
|  | result = mixin("get!(int) " ~ op ~ " other.get!(int)"); | 
|  | else if (convertsTo!(ulong) && other.convertsTo!(ulong)) | 
|  | result = mixin("get!(ulong) " ~ op ~ " other.get!(ulong)"); | 
|  | else | 
|  | result = mixin("get!(long) " ~ op ~ " other.get!(long)"); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (is(typeof(T.max) : uint) && T.min == 0 && convertsTo!(uint)) | 
|  | result = mixin("get!(uint) " ~ op ~ " other"); | 
|  | else if (is(typeof(T.max) : int) && T.min < 0 && convertsTo!(int)) | 
|  | result = mixin("get!(int) " ~ op ~ " other"); | 
|  | else if (is(typeof(T.max) : ulong) && T.min == 0 | 
|  | && convertsTo!(ulong)) | 
|  | result = mixin("get!(ulong) " ~ op ~ " other"); | 
|  | else | 
|  | result = mixin("get!(long) " ~ op ~ " other"); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Arithmetic between `VariantN` objects and numeric | 
|  | * values. All arithmetic operations return a `VariantN` | 
|  | * object typed depending on the types of both values | 
|  | * involved. The conversion rules mimic D's built-in rules for | 
|  | * arithmetic conversions. | 
|  | */ | 
|  | VariantN opBinary(string op, T)(T rhs) | 
|  | if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "^^" || op == "%") && | 
|  | is(typeof(opArithmetic!(T, op)(rhs)))) | 
|  | { return opArithmetic!(T, op)(rhs); } | 
|  | ///ditto | 
|  | VariantN opBinary(string op, T)(T rhs) | 
|  | if ((op == "&" || op == "|" || op == "^" || op == ">>" || op == "<<" || op == ">>>") && | 
|  | is(typeof(opLogic!(T, op)(rhs)))) | 
|  | { return opLogic!(T, op)(rhs); } | 
|  | ///ditto | 
|  | VariantN opBinaryRight(string op, T)(T lhs) | 
|  | if ((op == "+" || op == "*") && | 
|  | is(typeof(opArithmetic!(T, op)(lhs)))) | 
|  | { return opArithmetic!(T, op)(lhs); } | 
|  | ///ditto | 
|  | VariantN opBinaryRight(string op, T)(T lhs) | 
|  | if ((op == "&" || op == "|" || op == "^") && | 
|  | is(typeof(opLogic!(T, op)(lhs)))) | 
|  | { return opLogic!(T, op)(lhs); } | 
|  | ///ditto | 
|  | VariantN opBinary(string op, T)(T rhs) | 
|  | if (op == "~") | 
|  | { | 
|  | auto temp = this; | 
|  | temp ~= rhs; | 
|  | return temp; | 
|  | } | 
|  | // ///ditto | 
|  | // VariantN opBinaryRight(string op, T)(T rhs) | 
|  | //     if (op == "~") | 
|  | // { | 
|  | //     VariantN temp = rhs; | 
|  | //     temp ~= this; | 
|  | //     return temp; | 
|  | // } | 
|  |  | 
|  | ///ditto | 
|  | VariantN opOpAssign(string op, T)(T rhs) | 
|  | { | 
|  | static if (op != "~") | 
|  | { | 
|  | mixin("return this = this" ~ op ~ "rhs;"); | 
|  | } | 
|  | else | 
|  | { | 
|  | auto toAppend = Variant(rhs); | 
|  | fptr(OpID.catAssign, &store, &toAppend) == 0 || assert(false); | 
|  | return this; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Array and associative array operations. If a $(D | 
|  | * VariantN) contains an (associative) array, it can be indexed | 
|  | * into. Otherwise, an exception is thrown. | 
|  | */ | 
|  | inout(Variant) opIndex(K)(K i) inout | 
|  | { | 
|  | auto result = Variant(i); | 
|  | fptr(OpID.index, cast(ubyte[size]*) &store, &result) == 0 || assert(false); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /// | 
|  | version (StdDdoc) | 
|  | @system unittest | 
|  | { | 
|  | Variant a = new int[10]; | 
|  | a[5] = 42; | 
|  | assert(a[5] == 42); | 
|  | a[5] += 8; | 
|  | assert(a[5] == 50); | 
|  |  | 
|  | int[int] hash = [ 42:24 ]; | 
|  | a = hash; | 
|  | assert(a[42] == 24); | 
|  | a[42] /= 2; | 
|  | assert(a[42] == 12); | 
|  | } | 
|  |  | 
|  | /// ditto | 
|  | Variant opIndexAssign(T, N)(T value, N i) | 
|  | { | 
|  | static if (AllowedTypes.length && !isInstanceOf!(.VariantN, T)) | 
|  | { | 
|  | enum canAssign(U) = __traits(compiles, (U u){ u[i] = value; }); | 
|  | static assert(anySatisfy!(canAssign, AllowedTypes), | 
|  | "Cannot assign " ~ T.stringof ~ " to " ~ VariantN.stringof ~ | 
|  | " indexed with " ~ N.stringof); | 
|  | } | 
|  | Variant[2] args = [ Variant(value), Variant(i) ]; | 
|  | fptr(OpID.indexAssign, &store, &args) == 0 || assert(false); | 
|  | return args[0]; | 
|  | } | 
|  |  | 
|  | /// ditto | 
|  | Variant opIndexOpAssign(string op, T, N)(T value, N i) | 
|  | { | 
|  | return opIndexAssign(mixin(`opIndex(i)` ~ op ~ `value`), i); | 
|  | } | 
|  |  | 
|  | /** If the `VariantN` contains an (associative) array, | 
|  | * returns the _length of that array. Otherwise, throws an | 
|  | * exception. | 
|  | */ | 
|  | @property size_t length() | 
|  | { | 
|  | return cast(size_t) fptr(OpID.length, &store, null); | 
|  | } | 
|  |  | 
|  | /** | 
|  | If the `VariantN` contains an array, applies `dg` to each | 
|  | element of the array in turn. Otherwise, throws an exception. | 
|  | */ | 
|  | int opApply(Delegate)(scope Delegate dg) if (is(Delegate == delegate)) | 
|  | { | 
|  | alias A = Parameters!(Delegate)[0]; | 
|  | if (type == typeid(A[])) | 
|  | { | 
|  | auto arr = get!(A[]); | 
|  | foreach (ref e; arr) | 
|  | { | 
|  | if (dg(e)) return 1; | 
|  | } | 
|  | } | 
|  | else static if (is(A == VariantN)) | 
|  | { | 
|  | foreach (i; 0 .. length) | 
|  | { | 
|  | // @@@TODO@@@: find a better way to not confuse | 
|  | // clients who think they change values stored in the | 
|  | // Variant when in fact they are only changing tmp. | 
|  | auto tmp = this[i]; | 
|  | debug scope(exit) assert(tmp == this[i]); | 
|  | if (dg(tmp)) return 1; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | import std.conv : text; | 
|  | import std.exception : enforce; | 
|  | enforce(false, text("Variant type ", type, | 
|  | " not iterable with values of type ", | 
|  | A.stringof)); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// | 
|  | @system unittest | 
|  | { | 
|  | alias Var = VariantN!(maxSize!(int, double, string)); | 
|  |  | 
|  | Var a; // Must assign before use, otherwise exception ensues | 
|  | // Initialize with an integer; make the type int | 
|  | Var 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 | 
|  | @system unittest | 
|  | { | 
|  | alias Var = VariantN!(maxSize!(int[])); | 
|  |  | 
|  | Var a = new int[42]; | 
|  | assert(a.length == 42); | 
|  | a[5] = 7; | 
|  | assert(a[5] == 7); | 
|  | } | 
|  |  | 
|  | @safe unittest | 
|  | { | 
|  | alias V = VariantN!24; | 
|  | const alignMask = V.alignof - 1; | 
|  | assert(V.sizeof == ((24 + (void*).sizeof + alignMask) & ~alignMask)); | 
|  | } | 
|  |  | 
|  | /// Can also assign class values | 
|  | @system unittest | 
|  | { | 
|  | alias Var = VariantN!(maxSize!(int*)); // classes are pointers | 
|  | Var a; | 
|  |  | 
|  | class Foo {} | 
|  | auto foo = new Foo; | 
|  | a = foo; | 
|  | assert(*a.peek!(Foo) == foo); // and full type information is preserved | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | import std.conv : to; | 
|  | Variant v; | 
|  | int foo() { return 42; } | 
|  | v = &foo; | 
|  | assert(v() == 42); | 
|  |  | 
|  | static int bar(string s) { return to!int(s); } | 
|  | v = &bar; | 
|  | assert(v("43") == 43); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | int[int] hash = [ 42:24 ]; | 
|  | Variant v = hash; | 
|  | assert(v[42] == 24); | 
|  | v[42] = 5; | 
|  | assert(v[42] == 5); | 
|  | } | 
|  |  | 
|  | // opIndex with static arrays, https://issues.dlang.org/show_bug.cgi?id=12771 | 
|  | @system unittest | 
|  | { | 
|  | int[4] elements = [0, 1, 2, 3]; | 
|  | Variant v = elements; | 
|  | assert(v == elements); | 
|  | assert(v[2] == 2); | 
|  | assert(v[3] == 3); | 
|  | v[2] = 6; | 
|  | assert(v[2] == 6); | 
|  | assert(v != elements); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | import std.exception : assertThrown; | 
|  | Algebraic!(int[]) v = [2, 2]; | 
|  |  | 
|  | assert(v == [2, 2]); | 
|  | v[0] = 1; | 
|  | assert(v[0] == 1); | 
|  | assert(v != [2, 2]); | 
|  |  | 
|  | // opIndexAssign from Variant | 
|  | v[1] = v[0]; | 
|  | assert(v[1] == 1); | 
|  |  | 
|  | static assert(!__traits(compiles, (v[1] = null))); | 
|  | assertThrown!VariantException(v[1] = Variant(null)); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=10879 | 
|  | @system unittest | 
|  | { | 
|  | int[10] arr = [1,2,3,4,5,6,7,8,9,10]; | 
|  | Variant v1 = arr; | 
|  | Variant v2; | 
|  | v2 = arr; | 
|  | assert(v1 == arr); | 
|  | assert(v2 == arr); | 
|  | foreach (i, e; arr) | 
|  | { | 
|  | assert(v1[i] == e); | 
|  | assert(v2[i] == e); | 
|  | } | 
|  | static struct LargeStruct | 
|  | { | 
|  | int[100] data; | 
|  | } | 
|  | LargeStruct ls; | 
|  | ls.data[] = 4; | 
|  | v1 = ls; | 
|  | Variant v3 = ls; | 
|  | assert(v1 == ls); | 
|  | assert(v3 == ls); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=8195 | 
|  | @system unittest | 
|  | { | 
|  | struct S | 
|  | { | 
|  | int a; | 
|  | long b; | 
|  | string c; | 
|  | real d = 0.0; | 
|  | bool e; | 
|  | } | 
|  |  | 
|  | static assert(S.sizeof >= Variant.sizeof); | 
|  | alias Types = AliasSeq!(string, int, S); | 
|  | alias MyVariant = VariantN!(maxSize!Types, Types); | 
|  |  | 
|  | auto v = MyVariant(S.init); | 
|  | assert(v == S.init); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=10961 | 
|  | @system unittest | 
|  | { | 
|  | // Primarily test that we can assign a void[] to a Variant. | 
|  | void[] elements = cast(void[])[1, 2, 3]; | 
|  | Variant v = elements; | 
|  | void[] returned = v.get!(void[]); | 
|  | assert(returned == elements); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=13352 | 
|  | @system unittest | 
|  | { | 
|  | alias TP = Algebraic!(long); | 
|  | auto a = TP(1L); | 
|  | auto b = TP(2L); | 
|  | assert(!TP.allowed!ulong); | 
|  | assert(a + b == 3L); | 
|  | assert(a + 2 == 3L); | 
|  | assert(1 + b == 3L); | 
|  |  | 
|  | alias TP2 = Algebraic!(long, string); | 
|  | auto c = TP2(3L); | 
|  | assert(a + c == 4L); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=13354 | 
|  | @system unittest | 
|  | { | 
|  | alias A = Algebraic!(string[]); | 
|  | A a = ["a", "b"]; | 
|  | assert(a[0] == "a"); | 
|  | assert(a[1] == "b"); | 
|  | a[1] = "c"; | 
|  | assert(a[1] == "c"); | 
|  |  | 
|  | alias AA = Algebraic!(int[string]); | 
|  | AA aa = ["a": 1, "b": 2]; | 
|  | assert(aa["a"] == 1); | 
|  | assert(aa["b"] == 2); | 
|  | aa["b"] = 3; | 
|  | assert(aa["b"] == 3); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=14198 | 
|  | @system unittest | 
|  | { | 
|  | Variant a = true; | 
|  | assert(a.type == typeid(bool)); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=14233 | 
|  | @system unittest | 
|  | { | 
|  | alias Atom = Algebraic!(string, This[]); | 
|  |  | 
|  | Atom[] values = []; | 
|  | auto a = Atom(values); | 
|  | } | 
|  |  | 
|  | pure nothrow @nogc | 
|  | @system unittest | 
|  | { | 
|  | Algebraic!(int, double) a; | 
|  | a = 100; | 
|  | a = 1.0; | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=14457 | 
|  | @system unittest | 
|  | { | 
|  | alias A = Algebraic!(int, float, double); | 
|  | alias B = Algebraic!(int, float); | 
|  |  | 
|  | A a = 1; | 
|  | B b = 6f; | 
|  | a = b; | 
|  |  | 
|  | assert(a.type == typeid(float)); | 
|  | assert(a.get!float == 6f); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=14585 | 
|  | @system unittest | 
|  | { | 
|  | static struct S | 
|  | { | 
|  | int x = 42; | 
|  | ~this() {assert(x == 42);} | 
|  | } | 
|  | Variant(S()).get!S; | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=14586 | 
|  | @system unittest | 
|  | { | 
|  | const Variant v = new immutable Object; | 
|  | v.get!(immutable Object); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | static struct S | 
|  | { | 
|  | T opCast(T)() {assert(false);} | 
|  | } | 
|  | Variant v = S(); | 
|  | v.get!S; | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=13262 | 
|  | @system unittest | 
|  | { | 
|  | static void fun(T)(Variant v){ | 
|  | T x; | 
|  | v = x; | 
|  | auto r = v.get!(T); | 
|  | } | 
|  | Variant v; | 
|  | fun!(shared(int))(v); | 
|  | fun!(shared(int)[])(v); | 
|  |  | 
|  | static struct S1 | 
|  | { | 
|  | int c; | 
|  | string a; | 
|  | } | 
|  |  | 
|  | static struct S2 | 
|  | { | 
|  | string a; | 
|  | shared int[] b; | 
|  | } | 
|  |  | 
|  | static struct S3 | 
|  | { | 
|  | string a; | 
|  | shared int[] b; | 
|  | int c; | 
|  | } | 
|  |  | 
|  | fun!(S1)(v); | 
|  | fun!(shared(S1))(v); | 
|  | fun!(S2)(v); | 
|  | fun!(shared(S2))(v); | 
|  | fun!(S3)(v); | 
|  | fun!(shared(S3))(v); | 
|  |  | 
|  | // ensure structs that are shared, but don't have shared postblits | 
|  | // can't be used. | 
|  | static struct S4 | 
|  | { | 
|  | int x; | 
|  | this(this) {x = 0;} | 
|  | } | 
|  |  | 
|  | fun!(S4)(v); | 
|  | static assert(!is(typeof(fun!(shared(S4))(v)))); | 
|  | } | 
|  |  | 
|  | @safe unittest | 
|  | { | 
|  | Algebraic!(int) x; | 
|  |  | 
|  | static struct SafeS | 
|  | { | 
|  | @safe ~this() {} | 
|  | } | 
|  |  | 
|  | Algebraic!(SafeS) y; | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=19986 | 
|  | @system unittest | 
|  | { | 
|  | VariantN!32 v; | 
|  | v = const(ubyte[33]).init; | 
|  |  | 
|  | struct S | 
|  | { | 
|  | ubyte[33] s; | 
|  | } | 
|  |  | 
|  | VariantN!32 v2; | 
|  | v2 = const(S).init; | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=21021 | 
|  | @system unittest | 
|  | { | 
|  | static struct S | 
|  | { | 
|  | int h; | 
|  | int[5] array; | 
|  | alias h this; | 
|  | } | 
|  |  | 
|  | S msg; | 
|  | msg.array[] = 3; | 
|  | Variant a = msg; | 
|  | auto other = a.get!S; | 
|  | assert(msg.array[0] == 3); | 
|  | assert(other.array[0] == 3); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=21231 | 
|  | // Compatibility with -preview=fieldwise | 
|  | @system unittest | 
|  | { | 
|  | static struct Empty | 
|  | { | 
|  | bool opCmp(const scope ref Empty) const | 
|  | { return false; } | 
|  | } | 
|  |  | 
|  | Empty a, b; | 
|  | assert(a == b); | 
|  | assert(!(a < b)); | 
|  |  | 
|  | VariantN!(4, Empty) v = a; | 
|  | assert(v == b); | 
|  | assert(!(v < b)); | 
|  | } | 
|  |  | 
|  | // Compatibility with -preview=fieldwise | 
|  | @system unittest | 
|  | { | 
|  | static struct Empty | 
|  | { | 
|  | bool opEquals(const scope ref Empty) const | 
|  | { return false; } | 
|  | } | 
|  |  | 
|  | Empty a, b; | 
|  | assert(a != b); | 
|  |  | 
|  | VariantN!(4, Empty) v = a; | 
|  | assert(v != b); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=22647 | 
|  | // Can compare with 'null' | 
|  | @system unittest | 
|  | { | 
|  | static struct Bar | 
|  | { | 
|  | int* ptr; | 
|  | alias ptr this; | 
|  | } | 
|  |  | 
|  | static class Foo {} | 
|  | int* iptr; | 
|  | int[] arr; | 
|  |  | 
|  | Variant v = Foo.init; // 'null' | 
|  | assert(v != null); // can only compare objects with 'null' by using 'is' | 
|  |  | 
|  | v = iptr; | 
|  | assert(v == null); // pointers can be compared with 'null' | 
|  |  | 
|  | v = arr; | 
|  | assert(v == null); // arrays can be compared with 'null' | 
|  |  | 
|  | v = ""; | 
|  | assert(v == null); // strings are arrays, an empty string is considered 'null' | 
|  |  | 
|  | v = Bar.init; | 
|  | assert(v == null); // works with alias this | 
|  |  | 
|  | v = [3]; | 
|  | assert(v != null); | 
|  | assert(v > null); | 
|  | assert(v >= null); | 
|  | assert(!(v < null)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | _Algebraic data type restricted to a closed set of possible | 
|  | types. It's an alias for $(LREF VariantN) with an | 
|  | appropriately-constructed maximum size. `Algebraic` is | 
|  | useful when it is desirable to restrict what a discriminated type | 
|  | could hold to the end of defining simpler and more efficient | 
|  | manipulation. | 
|  |  | 
|  | $(RED Warning: $(LREF Algebraic) is outdated and not recommended for use in new | 
|  | code. Instead, use $(REF SumType, std,sumtype).) | 
|  | */ | 
|  | template Algebraic(T...) | 
|  | { | 
|  | alias Algebraic = VariantN!(maxSize!T, T); | 
|  | } | 
|  |  | 
|  | /// | 
|  | @system unittest | 
|  | { | 
|  | auto v = Algebraic!(int, double, string)(5); | 
|  | assert(v.peek!(int)); | 
|  | v = 3.14; | 
|  | assert(v.peek!(double)); | 
|  | // auto x = v.peek!(long); // won't compile, type long not allowed | 
|  | // v = '1'; // won't compile, type char not allowed | 
|  | } | 
|  |  | 
|  | /** | 
|  | $(H4 Self-Referential Types) | 
|  |  | 
|  | A useful and popular use of algebraic data structures is for defining $(LUCKY | 
|  | self-referential data structures), i.e. structures that embed references to | 
|  | values of their own type within. | 
|  |  | 
|  | This is achieved with `Algebraic` by using `This` as a placeholder whenever a | 
|  | reference to the type being defined is needed. The `Algebraic` instantiation | 
|  | will perform $(LINK2 https://en.wikipedia.org/wiki/Name_resolution_(programming_languages)#Alpha_renaming_to_make_name_resolution_trivial, | 
|  | alpha renaming) on its constituent types, replacing `This` | 
|  | with the self-referenced type. The structure of the type involving `This` may | 
|  | be arbitrarily complex. | 
|  | */ | 
|  | @system unittest | 
|  | { | 
|  | import std.typecons : Tuple, tuple; | 
|  |  | 
|  | // A tree is either a leaf or a branch of two other trees | 
|  | alias Tree(Leaf) = Algebraic!(Leaf, Tuple!(This*, This*)); | 
|  | Tree!int tree = tuple(new Tree!int(42), new Tree!int(43)); | 
|  | Tree!int* right = tree.get!1[1]; | 
|  | assert(*right == 43); | 
|  |  | 
|  | // An object is a double, a string, or a hash of objects | 
|  | alias Obj = Algebraic!(double, string, This[string]); | 
|  | Obj obj = "hello"; | 
|  | assert(obj.get!1 == "hello"); | 
|  | obj = 42.0; | 
|  | assert(obj.get!0 == 42); | 
|  | obj = ["customer": Obj("John"), "paid": Obj(23.95)]; | 
|  | assert(obj.get!2["customer"] == "John"); | 
|  | } | 
|  |  | 
|  | private struct FakeComplexReal | 
|  | { | 
|  | real re, im; | 
|  | } | 
|  |  | 
|  | /** | 
|  | Alias for $(LREF VariantN) instantiated with the largest size of `creal`, | 
|  | `char[]`, and `void delegate()`. This ensures that `Variant` is large enough | 
|  | to hold all of D's predefined types unboxed, including all numeric types, | 
|  | pointers, delegates, and class references.  You may want to use | 
|  | `VariantN` directly with a different maximum size either for | 
|  | storing larger types unboxed, or for saving memory. | 
|  | */ | 
|  | alias Variant = VariantN!(maxSize!(FakeComplexReal, char[], void delegate())); | 
|  |  | 
|  | /// | 
|  | @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 | 
|  | @system unittest | 
|  | { | 
|  | Variant a = new int[42]; | 
|  | assert(a.length == 42); | 
|  | a[5] = 7; | 
|  | assert(a[5] == 7); | 
|  | } | 
|  |  | 
|  | /// Can also assign class values | 
|  | @system unittest | 
|  | { | 
|  | Variant a; | 
|  |  | 
|  | class Foo {} | 
|  | auto foo = new Foo; | 
|  | a = foo; | 
|  | assert(*a.peek!(Foo) == foo); // and full type information is preserved | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns an array of variants constructed from `args`. | 
|  | * | 
|  | * This is by design. During construction the `Variant` needs | 
|  | * static type information about the type being held, so as to store a | 
|  | * pointer to function for fast retrieval. | 
|  | */ | 
|  | Variant[] variantArray(T...)(T args) | 
|  | { | 
|  | Variant[] result; | 
|  | foreach (arg; args) | 
|  | { | 
|  | result ~= Variant(arg); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /// | 
|  | @system unittest | 
|  | { | 
|  | auto a = variantArray(1, 3.14, "Hi!"); | 
|  | assert(a[1] == 3.14); | 
|  | auto b = Variant(a); // variant array as variant | 
|  | assert(b[1] == 3.14); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Thrown in three cases: | 
|  | * | 
|  | * $(OL $(LI An uninitialized `Variant` is used in any way except | 
|  | * assignment and `hasValue`;) $(LI A `get` or | 
|  | * `coerce` is attempted with an incompatible target type;) | 
|  | * $(LI A comparison between `Variant` objects of | 
|  | * incompatible types is attempted.)) | 
|  | * | 
|  | */ | 
|  |  | 
|  | // @@@ BUG IN COMPILER. THE 'STATIC' BELOW SHOULD NOT COMPILE | 
|  | static class VariantException : Exception | 
|  | { | 
|  | /// The source type in the conversion or comparison | 
|  | TypeInfo source; | 
|  | /// The target type in the conversion or comparison | 
|  | TypeInfo target; | 
|  | this(string s) | 
|  | { | 
|  | super(s); | 
|  | } | 
|  | this(TypeInfo source, TypeInfo target) | 
|  | { | 
|  | super("Variant: attempting to use incompatible types " | 
|  | ~ source.toString() | 
|  | ~ " and " ~ target.toString()); | 
|  | this.source = source; | 
|  | this.target = target; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// | 
|  | @system unittest | 
|  | { | 
|  | import std.exception : assertThrown; | 
|  |  | 
|  | Variant v; | 
|  |  | 
|  | // uninitialized use | 
|  | assertThrown!VariantException(v + 1); | 
|  | assertThrown!VariantException(v.length); | 
|  |  | 
|  | // .get with an incompatible target type | 
|  | assertThrown!VariantException(Variant("a").get!int); | 
|  |  | 
|  | // comparison between incompatible types | 
|  | assertThrown!VariantException(Variant(3) < Variant("a")); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | alias W1 = This2Variant!(char, int, This[int]); | 
|  | alias W2 = AliasSeq!(int, char[int]); | 
|  | static assert(is(W1 == W2)); | 
|  |  | 
|  | alias var_t = Algebraic!(void, string); | 
|  | var_t foo = "quux"; | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | alias A = Algebraic!(real, This[], This[int], This[This]); | 
|  | A v1, v2, v3; | 
|  | v2 = 5.0L; | 
|  | v3 = 42.0L; | 
|  | //v1 = [ v2 ][]; | 
|  | auto v = v1.peek!(A[]); | 
|  | //writeln(v[0]); | 
|  | v1 = [ 9 : v3 ]; | 
|  | //writeln(v1); | 
|  | v1 = [ v3 : v3 ]; | 
|  | //writeln(v1); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | import std.conv : ConvException; | 
|  | import std.exception : assertThrown, collectException; | 
|  | // try it with an oddly small size | 
|  | VariantN!(1) test; | 
|  | assert(test.size > 1); | 
|  |  | 
|  | // variantArray tests | 
|  | auto heterogeneous = variantArray(1, 4.5, "hi"); | 
|  | assert(heterogeneous.length == 3); | 
|  | auto variantArrayAsVariant = Variant(heterogeneous); | 
|  | assert(variantArrayAsVariant[0] == 1); | 
|  | assert(variantArrayAsVariant.length == 3); | 
|  |  | 
|  | // array tests | 
|  | auto arr = Variant([1.2].dup); | 
|  | auto e = arr[0]; | 
|  | assert(e == 1.2); | 
|  | arr[0] = 2.0; | 
|  | assert(arr[0] == 2); | 
|  | arr ~= 4.5; | 
|  | assert(arr[1] == 4.5); | 
|  |  | 
|  | // general tests | 
|  | Variant a; | 
|  | auto b = Variant(5); | 
|  | assert(!b.peek!(real) && b.peek!(int)); | 
|  | // assign | 
|  | a = *b.peek!(int); | 
|  | // comparison | 
|  | assert(a == b, a.type.toString() ~ " " ~ b.type.toString()); | 
|  | auto c = Variant("this is a string"); | 
|  | assert(a != c); | 
|  | // comparison via implicit conversions | 
|  | a = 42; b = 42.0; assert(a == b); | 
|  |  | 
|  | // try failing conversions | 
|  | bool failed = false; | 
|  | try | 
|  | { | 
|  | auto d = c.get!(int); | 
|  | } | 
|  | catch (Exception e) | 
|  | { | 
|  | //writeln(stderr, e.toString); | 
|  | failed = true; | 
|  | } | 
|  | assert(failed); // :o) | 
|  |  | 
|  | // toString tests | 
|  | a = Variant(42); assert(a.toString() == "42"); | 
|  | a = Variant(42.22); assert(a.toString() == "42.22"); | 
|  |  | 
|  | // coerce tests | 
|  | a = Variant(42.22); assert(a.coerce!(int) == 42); | 
|  | a = cast(short) 5; assert(a.coerce!(double) == 5); | 
|  | a = Variant("10"); assert(a.coerce!int == 10); | 
|  |  | 
|  | a = Variant(1); | 
|  | assert(a.coerce!bool); | 
|  | a = Variant(0); | 
|  | assert(!a.coerce!bool); | 
|  |  | 
|  | a = Variant(1.0); | 
|  | assert(a.coerce!bool); | 
|  | a = Variant(0.0); | 
|  | assert(!a.coerce!bool); | 
|  | a = Variant(float.init); | 
|  | assertThrown!ConvException(a.coerce!bool); | 
|  |  | 
|  | a = Variant("true"); | 
|  | assert(a.coerce!bool); | 
|  | a = Variant("false"); | 
|  | assert(!a.coerce!bool); | 
|  | a = Variant(""); | 
|  | assertThrown!ConvException(a.coerce!bool); | 
|  |  | 
|  | // Object tests | 
|  | class B1 {} | 
|  | class B2 : B1 {} | 
|  | a = new B2; | 
|  | assert(a.coerce!(B1) !is null); | 
|  | a = new B1; | 
|  | assert(collectException(a.coerce!(B2) is null)); | 
|  | a = cast(Object) new B2; // lose static type info; should still work | 
|  | assert(a.coerce!(B2) !is null); | 
|  |  | 
|  | //     struct Big { int a[45]; } | 
|  | //     a = Big.init; | 
|  |  | 
|  | // hash | 
|  | assert(a.toHash() != 0); | 
|  | } | 
|  |  | 
|  | // tests adapted from | 
|  | // http://www.dsource.org/projects/tango/browser/trunk/tango/core/Variant.d?rev=2601 | 
|  | @system unittest | 
|  | { | 
|  | Variant v; | 
|  |  | 
|  | assert(!v.hasValue); | 
|  | v = 42; | 
|  | assert( v.peek!(int) ); | 
|  | assert( v.convertsTo!(long) ); | 
|  | assert( v.get!(int) == 42 ); | 
|  | assert( v.get!(long) == 42L ); | 
|  | assert( v.get!(ulong) == 42uL ); | 
|  |  | 
|  | v = "Hello, World!"; | 
|  | assert( v.peek!(string) ); | 
|  |  | 
|  | assert( v.get!(string) == "Hello, World!" ); | 
|  | assert(!is(char[] : wchar[])); | 
|  | assert( !v.convertsTo!(wchar[]) ); | 
|  | assert( v.get!(string) == "Hello, World!" ); | 
|  |  | 
|  | // Literal arrays are dynamically-typed | 
|  | v = cast(int[4]) [1,2,3,4]; | 
|  | assert( v.peek!(int[4]) ); | 
|  | assert( v.get!(int[4]) == [1,2,3,4] ); | 
|  |  | 
|  | { | 
|  | v = [1,2,3,4,5]; | 
|  | assert( v.peek!(int[]) ); | 
|  | assert( v.get!(int[]) == [1,2,3,4,5] ); | 
|  | } | 
|  |  | 
|  | v = 3.1413; | 
|  | assert( v.peek!(double) ); | 
|  | assert( v.convertsTo!(real) ); | 
|  | //@@@ BUG IN COMPILER: DOUBLE SHOULD NOT IMPLICITLY CONVERT TO FLOAT | 
|  | assert( v.convertsTo!(float) ); | 
|  | assert( *v.peek!(double) == 3.1413 ); | 
|  |  | 
|  | auto u = Variant(v); | 
|  | assert( u.peek!(double) ); | 
|  | assert( *u.peek!(double) == 3.1413 ); | 
|  |  | 
|  | // operators | 
|  | v = 38; | 
|  | assert( v + 4 == 42 ); | 
|  | assert( 4 + v == 42 ); | 
|  | assert( v - 4 == 34 ); | 
|  | assert( Variant(4) - v == -34 ); | 
|  | assert( v * 2 == 76 ); | 
|  | assert( 2 * v == 76 ); | 
|  | assert( v / 2 == 19 ); | 
|  | assert( Variant(2) / v == 0 ); | 
|  | assert( v % 2 == 0 ); | 
|  | assert( Variant(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( Variant(1) << Variant(2) == 4 ); | 
|  | assert( v >> 1 == 19 ); | 
|  | assert( Variant(4) >> Variant(2) == 1 ); | 
|  | assert( Variant("abc") ~ "def" == "abcdef" ); | 
|  | assert( Variant("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 = 38; v += 1;  assert( v < 40 ); | 
|  |  | 
|  | v = "abc"; | 
|  | v ~= "def"; | 
|  | assert( v == "abcdef", *v.peek!(char[]) ); | 
|  | assert( Variant(0) < Variant(42) ); | 
|  | assert( Variant(42) > Variant(0) ); | 
|  | assert( Variant(42) > Variant(0.1) ); | 
|  | assert( Variant(42.1) > Variant(1) ); | 
|  | 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"); | 
|  |  | 
|  | int[Variant] hash; | 
|  | hash[v1] = 0; | 
|  | hash[v2] = 1; | 
|  |  | 
|  | assert( hash[v1] == 0 ); | 
|  | assert( hash[v2] == 1 ); | 
|  | } | 
|  |  | 
|  | { | 
|  | 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 ); | 
|  | } | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | // check comparisons incompatible with AllowedTypes | 
|  | Algebraic!int v = 2; | 
|  |  | 
|  | assert(v == 2); | 
|  | assert(v < 3); | 
|  | static assert(!__traits(compiles, () => v == long.max)); | 
|  | static assert(!__traits(compiles, () => v == null)); | 
|  | static assert(!__traits(compiles, () => v < long.max)); | 
|  | static assert(!__traits(compiles, () => v > null)); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=1558 | 
|  | @system unittest | 
|  | { | 
|  | Variant va=1; | 
|  | Variant vb=-2; | 
|  | assert((va+vb).get!(int) == -1); | 
|  | assert((va-vb).get!(int) == 3); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | Variant a; | 
|  | a=5; | 
|  | Variant b; | 
|  | b=a; | 
|  | Variant[] c; | 
|  | c = variantArray(1, 2, 3.0, "hello", 4); | 
|  | assert(c[3] == "hello"); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | Variant v = 5; | 
|  | assert(!__traits(compiles, v.coerce!(bool delegate()))); | 
|  | } | 
|  |  | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | struct Huge { | 
|  | real a, b, c, d, e, f, g; | 
|  | } | 
|  |  | 
|  | Huge huge; | 
|  | huge.e = 42; | 
|  | Variant v; | 
|  | v = huge;  // Compile time error. | 
|  | assert(v.get!(Huge).e == 42); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | const x = Variant(42); | 
|  | auto y1 = x.get!(const int); | 
|  | // @@@BUG@@@ | 
|  | //auto y2 = x.get!(immutable int)(); | 
|  | } | 
|  |  | 
|  | // test iteration | 
|  | @system unittest | 
|  | { | 
|  | auto v = Variant([ 1, 2, 3, 4 ][]); | 
|  | auto j = 0; | 
|  | foreach (int i; v) | 
|  | { | 
|  | assert(i == ++j); | 
|  | } | 
|  | assert(j == 4); | 
|  | } | 
|  |  | 
|  | // test convertibility | 
|  | @system unittest | 
|  | { | 
|  | auto v = Variant("abc".dup); | 
|  | assert(v.convertsTo!(char[])); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=5424 | 
|  | @system unittest | 
|  | { | 
|  | interface A { | 
|  | void func1(); | 
|  | } | 
|  | static class AC: A { | 
|  | void func1() { | 
|  | } | 
|  | } | 
|  |  | 
|  | A a = new AC(); | 
|  | a.func1(); | 
|  | Variant b = Variant(a); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=7070 | 
|  | @system unittest | 
|  | { | 
|  | Variant v; | 
|  | v = null; | 
|  | } | 
|  |  | 
|  | // Class and interface opEquals, https://issues.dlang.org/show_bug.cgi?id=12157 | 
|  | @system unittest | 
|  | { | 
|  | class Foo { } | 
|  |  | 
|  | class DerivedFoo : Foo { } | 
|  |  | 
|  | Foo f1 = new Foo(); | 
|  | Foo f2 = new DerivedFoo(); | 
|  |  | 
|  | Variant v1 = f1, v2 = f2; | 
|  | assert(v1 == f1); | 
|  | assert(v1 != new Foo()); | 
|  | assert(v1 != f2); | 
|  | assert(v2 != v1); | 
|  | assert(v2 == f2); | 
|  | } | 
|  |  | 
|  | // Const parameters with opCall, https://issues.dlang.org/show_bug.cgi?id=11361 | 
|  | @system unittest | 
|  | { | 
|  | static string t1(string c) { | 
|  | return c ~ "a"; | 
|  | } | 
|  |  | 
|  | static const(char)[] t2(const(char)[] p) { | 
|  | return p ~ "b"; | 
|  | } | 
|  |  | 
|  | static char[] t3(int p) { | 
|  | import std.conv : text; | 
|  | return p.text.dup; | 
|  | } | 
|  |  | 
|  | Variant v1 = &t1; | 
|  | Variant v2 = &t2; | 
|  | Variant v3 = &t3; | 
|  |  | 
|  | assert(v1("abc") == "abca"); | 
|  | assert(v1("abc").type == typeid(string)); | 
|  | assert(v2("abc") == "abcb"); | 
|  |  | 
|  | assert(v2(cast(char[])("abc".dup)) == "abcb"); | 
|  | assert(v2("abc").type == typeid(const(char)[])); | 
|  |  | 
|  | assert(v3(4) == ['4']); | 
|  | assert(v3(4).type == typeid(char[])); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=12071 | 
|  | @system unittest | 
|  | { | 
|  | static struct Structure { int data; } | 
|  | alias VariantTest = Algebraic!(Structure delegate() pure nothrow @nogc @safe); | 
|  |  | 
|  | bool called = false; | 
|  | Structure example() pure nothrow @nogc @safe | 
|  | { | 
|  | called = true; | 
|  | return Structure.init; | 
|  | } | 
|  | auto m = VariantTest(&example); | 
|  | m(); | 
|  | assert(called); | 
|  | } | 
|  |  | 
|  | // Ordering comparisons of incompatible types | 
|  | // e.g. https://issues.dlang.org/show_bug.cgi?id=7990 | 
|  | @system unittest | 
|  | { | 
|  | import std.exception : assertThrown; | 
|  | assertThrown!VariantException(Variant(3) < "a"); | 
|  | assertThrown!VariantException("a" < Variant(3)); | 
|  | assertThrown!VariantException(Variant(3) < Variant("a")); | 
|  |  | 
|  | assertThrown!VariantException(Variant.init < Variant(3)); | 
|  | assertThrown!VariantException(Variant(3) < Variant.init); | 
|  | } | 
|  |  | 
|  | // Handling of unordered types | 
|  | // https://issues.dlang.org/show_bug.cgi?id=9043 | 
|  | @system unittest | 
|  | { | 
|  | import std.exception : assertThrown; | 
|  | static struct A { int a; } | 
|  |  | 
|  | assert(Variant(A(3)) != A(4)); | 
|  |  | 
|  | assertThrown!VariantException(Variant(A(3)) < A(4)); | 
|  | assertThrown!VariantException(A(3) < Variant(A(4))); | 
|  | assertThrown!VariantException(Variant(A(3)) < Variant(A(4))); | 
|  | } | 
|  |  | 
|  | // Handling of empty types and arrays | 
|  | // https://issues.dlang.org/show_bug.cgi?id=10958 | 
|  | @system unittest | 
|  | { | 
|  | class EmptyClass { } | 
|  | struct EmptyStruct { } | 
|  | alias EmptyArray = void[0]; | 
|  | alias Alg = Algebraic!(EmptyClass, EmptyStruct, EmptyArray); | 
|  |  | 
|  | Variant testEmpty(T)() | 
|  | { | 
|  | T inst; | 
|  | Variant v = inst; | 
|  | assert(v.get!T == inst); | 
|  | assert(v.peek!T !is null); | 
|  | assert(*v.peek!T == inst); | 
|  | Alg alg = inst; | 
|  | assert(alg.get!T == inst); | 
|  | return v; | 
|  | } | 
|  |  | 
|  | testEmpty!EmptyClass(); | 
|  | testEmpty!EmptyStruct(); | 
|  | testEmpty!EmptyArray(); | 
|  |  | 
|  | // EmptyClass/EmptyStruct sizeof is 1, so we have this to test just size 0. | 
|  | EmptyArray arr = EmptyArray.init; | 
|  | Algebraic!(EmptyArray) a = arr; | 
|  | assert(a.length == 0); | 
|  | assert(a.get!EmptyArray == arr); | 
|  | } | 
|  |  | 
|  | // Handling of void function pointers / delegates | 
|  | // https://issues.dlang.org/show_bug.cgi?id=11360 | 
|  | @system unittest | 
|  | { | 
|  | static void t1() { } | 
|  | Variant v = &t1; | 
|  | assert(v() == Variant.init); | 
|  |  | 
|  | static int t2() { return 3; } | 
|  | Variant v2 = &t2; | 
|  | assert(v2() == 3); | 
|  | } | 
|  |  | 
|  | // Using peek for large structs | 
|  | // https://issues.dlang.org/show_bug.cgi?id=8580 | 
|  | @system unittest | 
|  | { | 
|  | struct TestStruct(bool pad) | 
|  | { | 
|  | int val1; | 
|  | static if (pad) | 
|  | ubyte[Variant.size] padding; | 
|  | int val2; | 
|  | } | 
|  |  | 
|  | void testPeekWith(T)() | 
|  | { | 
|  | T inst; | 
|  | inst.val1 = 3; | 
|  | inst.val2 = 4; | 
|  | Variant v = inst; | 
|  | T* original = v.peek!T; | 
|  | assert(original.val1 == 3); | 
|  | assert(original.val2 == 4); | 
|  | original.val1 = 6; | 
|  | original.val2 = 8; | 
|  | T modified = v.get!T; | 
|  | assert(modified.val1 == 6); | 
|  | assert(modified.val2 == 8); | 
|  | } | 
|  |  | 
|  | testPeekWith!(TestStruct!false)(); | 
|  | testPeekWith!(TestStruct!true)(); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=18780 | 
|  | @system unittest | 
|  | { | 
|  | int x = 7; | 
|  | Variant a = x; | 
|  | assert(a.convertsTo!ulong); | 
|  | assert(a.convertsTo!uint); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Applies a delegate or function to the given $(LREF Algebraic) depending on the held type, | 
|  | * ensuring that all types are handled by the visiting functions. | 
|  | * | 
|  | * The delegate or function having the currently held value as parameter is called | 
|  | * with `variant`'s current value. Visiting handlers are passed | 
|  | * in the template parameter list. | 
|  | * It is statically ensured that all held types of | 
|  | * `variant` are handled across all handlers. | 
|  | * `visit` allows delegates and static functions to be passed | 
|  | * as parameters. | 
|  | * | 
|  | * If a function with an untyped parameter is specified, this function is called | 
|  | * when the variant contains a type that does not match any other function. | 
|  | * This can be used to apply the same function across multiple possible types. | 
|  | * Exactly one generic function is allowed. | 
|  | * | 
|  | * If a function without parameters is specified, this function is called | 
|  | * when `variant` doesn't hold a value. Exactly one parameter-less function | 
|  | * is allowed. | 
|  | * | 
|  | * Duplicate overloads matching the same type in one of the visitors are disallowed. | 
|  | * | 
|  | * Returns: The return type of visit is deduced from the visiting functions and must be | 
|  | * the same across all overloads. | 
|  | * Throws: $(LREF VariantException) if `variant` doesn't hold a value and no | 
|  | * parameter-less fallback function is specified. | 
|  | */ | 
|  | template visit(Handlers...) | 
|  | if (Handlers.length > 0) | 
|  | { | 
|  | /// | 
|  | auto visit(VariantType)(VariantType variant) | 
|  | if (isAlgebraic!VariantType) | 
|  | { | 
|  | return visitImpl!(true, VariantType, Handlers)(variant); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// | 
|  | @system unittest | 
|  | { | 
|  | Algebraic!(int, string) variant; | 
|  |  | 
|  | variant = 10; | 
|  | assert(variant.visit!((string s) => cast(int) s.length, | 
|  | (int i)    => i)() | 
|  | == 10); | 
|  | variant = "string"; | 
|  | assert(variant.visit!((int i) => i, | 
|  | (string s) => cast(int) s.length)() | 
|  | == 6); | 
|  |  | 
|  | // Error function usage | 
|  | Algebraic!(int, string) emptyVar; | 
|  | auto rslt = emptyVar.visit!((string s) => cast(int) s.length, | 
|  | (int i)    => i, | 
|  | () => -1)(); | 
|  | assert(rslt == -1); | 
|  |  | 
|  | // Generic function usage | 
|  | Algebraic!(int, float, real) number = 2; | 
|  | assert(number.visit!(x => x += 1) == 3); | 
|  |  | 
|  | // Generic function for int/float with separate behavior for string | 
|  | Algebraic!(int, float, string) something = 2; | 
|  | assert(something.visit!((string s) => s.length, x => x) == 2); // generic | 
|  | something = "asdf"; | 
|  | assert(something.visit!((string s) => s.length, x => x) == 4); // string | 
|  |  | 
|  | // Generic handler and empty handler | 
|  | Algebraic!(int, float, real) empty2; | 
|  | assert(empty2.visit!(x => x + 1, () => -1) == -1); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | Algebraic!(size_t, string) variant; | 
|  |  | 
|  | // not all handled check | 
|  | static assert(!__traits(compiles, variant.visit!((size_t i){ })() )); | 
|  |  | 
|  | variant = cast(size_t) 10; | 
|  | auto which = 0; | 
|  | variant.visit!( (string s) => which = 1, | 
|  | (size_t i) => which = 0 | 
|  | )(); | 
|  |  | 
|  | // integer overload was called | 
|  | assert(which == 0); | 
|  |  | 
|  | // mustn't compile as generic Variant not supported | 
|  | Variant v; | 
|  | static assert(!__traits(compiles, v.visit!((string s) => which = 1, | 
|  | (size_t i) => which = 0 | 
|  | )() | 
|  | )); | 
|  |  | 
|  | static size_t func(string s) { | 
|  | return s.length; | 
|  | } | 
|  |  | 
|  | variant = "test"; | 
|  | assert( 4 == variant.visit!(func, | 
|  | (size_t i) => i | 
|  | )()); | 
|  |  | 
|  | Algebraic!(int, float, string) variant2 = 5.0f; | 
|  | // Shouldn' t compile as float not handled by visitor. | 
|  | static assert(!__traits(compiles, variant2.visit!( | 
|  | (int _) {}, | 
|  | (string _) {})())); | 
|  |  | 
|  | Algebraic!(size_t, string, float) variant3; | 
|  | variant3 = 10.0f; | 
|  | auto floatVisited = false; | 
|  |  | 
|  | assert(variant3.visit!( | 
|  | (float f) { floatVisited = true; return cast(size_t) f; }, | 
|  | func, | 
|  | (size_t i) { return i; } | 
|  | )() == 10); | 
|  | assert(floatVisited == true); | 
|  |  | 
|  | Algebraic!(float, string) variant4; | 
|  |  | 
|  | assert(variant4.visit!(func, (float f) => cast(size_t) f, () => size_t.max)() == size_t.max); | 
|  |  | 
|  | // double error func check | 
|  | static assert(!__traits(compiles, | 
|  | visit!(() => size_t.max, func, (float f) => cast(size_t) f, () => size_t.max)(variant4)) | 
|  | ); | 
|  | } | 
|  |  | 
|  | // disallow providing multiple generic handlers to visit | 
|  | // disallow a generic handler that does not apply to all types | 
|  | @system unittest | 
|  | { | 
|  | Algebraic!(int, float) number = 2; | 
|  | // ok, x + 1 valid for int and float | 
|  | static assert( __traits(compiles, number.visit!(x => x + 1))); | 
|  | // bad, two generic handlers | 
|  | static assert(!__traits(compiles, number.visit!(x => x + 1, x => x + 2))); | 
|  | // bad, x ~ "a" does not apply to int or float | 
|  | static assert(!__traits(compiles, number.visit!(x => x ~ "a"))); | 
|  | // bad, x ~ "a" does not apply to int or float | 
|  | static assert(!__traits(compiles, number.visit!(x => x + 1, x => x ~ "a"))); | 
|  |  | 
|  | Algebraic!(int, string) maybenumber = 2; | 
|  | // ok, x ~ "a" valid for string, x + 1 valid for int, only 1 generic | 
|  | static assert( __traits(compiles, maybenumber.visit!((string x) => x ~ "a", x => "foobar"[0 .. x + 1]))); | 
|  | // bad, x ~ "a" valid for string but not int | 
|  | static assert(!__traits(compiles, maybenumber.visit!(x => x ~ "a"))); | 
|  | // bad, two generics, each only applies in one case | 
|  | static assert(!__traits(compiles, maybenumber.visit!(x => x + 1, x => x ~ "a"))); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Behaves as $(LREF visit) but doesn't enforce that all types are handled | 
|  | * by the visiting functions. | 
|  | * | 
|  | * If a parameter-less function is specified it is called when | 
|  | * either `variant` doesn't hold a value or holds a type | 
|  | * which isn't handled by the visiting functions. | 
|  | * | 
|  | * Returns: The return type of tryVisit is deduced from the visiting functions and must be | 
|  | * the same across all overloads. | 
|  | * Throws: $(LREF VariantException) if `variant` doesn't hold a value or | 
|  | * `variant` holds a value which isn't handled by the visiting functions, | 
|  | * when no parameter-less fallback function is specified. | 
|  | */ | 
|  | template tryVisit(Handlers...) | 
|  | if (Handlers.length > 0) | 
|  | { | 
|  | /// | 
|  | auto tryVisit(VariantType)(VariantType variant) | 
|  | if (isAlgebraic!VariantType) | 
|  | { | 
|  | return visitImpl!(false, VariantType, Handlers)(variant); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// | 
|  | @system unittest | 
|  | { | 
|  | Algebraic!(int, string) variant; | 
|  |  | 
|  | variant = 10; | 
|  | auto which = -1; | 
|  | variant.tryVisit!((int i) { which = 0; })(); | 
|  | assert(which == 0); | 
|  |  | 
|  | // Error function usage | 
|  | variant = "test"; | 
|  | variant.tryVisit!((int i) { which = 0; }, | 
|  | ()      { which = -100; })(); | 
|  | assert(which == -100); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | import std.exception : assertThrown; | 
|  | Algebraic!(int, string) variant; | 
|  |  | 
|  | variant = 10; | 
|  | auto which = -1; | 
|  | variant.tryVisit!((int i){ which = 0; })(); | 
|  |  | 
|  | assert(which == 0); | 
|  |  | 
|  | variant = "test"; | 
|  |  | 
|  | assertThrown!VariantException(variant.tryVisit!((int i) { which = 0; })()); | 
|  |  | 
|  | void errorfunc() | 
|  | { | 
|  | which = -1; | 
|  | } | 
|  |  | 
|  | variant.tryVisit!((int i) { which = 0; }, errorfunc)(); | 
|  |  | 
|  | assert(which == -1); | 
|  | } | 
|  |  | 
|  | private template isAlgebraic(Type) | 
|  | { | 
|  | static if (is(Type _ == VariantN!T, T...)) | 
|  | enum isAlgebraic = T.length >= 2; // T[0] == maxDataSize, T[1..$] == AllowedTypesParam | 
|  | else | 
|  | enum isAlgebraic = false; | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | static assert(!isAlgebraic!(Variant)); | 
|  | static assert( isAlgebraic!(Algebraic!(string))); | 
|  | static assert( isAlgebraic!(Algebraic!(int, int[]))); | 
|  | } | 
|  |  | 
|  | private auto visitImpl(bool Strict, VariantType, Handler...)(VariantType variant) | 
|  | if (isAlgebraic!VariantType && Handler.length > 0) | 
|  | { | 
|  | alias AllowedTypes = VariantType.AllowedTypes; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Returns: Struct where `indices`  is an array which | 
|  | * contains at the n-th position the index in Handler which takes the | 
|  | * n-th type of AllowedTypes. If an Handler doesn't match an | 
|  | * AllowedType, -1 is set. If a function in the delegates doesn't | 
|  | * have parameters, the field `exceptionFuncIdx` is set; | 
|  | * otherwise it's -1. | 
|  | */ | 
|  | auto visitGetOverloadMap() | 
|  | { | 
|  | struct Result { | 
|  | int[AllowedTypes.length] indices; | 
|  | int exceptionFuncIdx = -1; | 
|  | int generalFuncIdx = -1; | 
|  | } | 
|  |  | 
|  | Result result; | 
|  |  | 
|  | enum int nonmatch = () | 
|  | { | 
|  | foreach (int dgidx, dg; Handler) | 
|  | { | 
|  | bool found = false; | 
|  | foreach (T; AllowedTypes) | 
|  | { | 
|  | found |= __traits(compiles, { static assert(isSomeFunction!(dg!T)); }); | 
|  | found |= __traits(compiles, (T t) { dg(t); }); | 
|  | found |= __traits(compiles, dg()); | 
|  | } | 
|  | if (!found) return dgidx; | 
|  | } | 
|  | return -1; | 
|  | }(); | 
|  | static assert(nonmatch == -1, "No match for visit handler #"~ | 
|  | nonmatch.stringof~" ("~Handler[nonmatch].stringof~")"); | 
|  |  | 
|  | foreach (tidx, T; AllowedTypes) | 
|  | { | 
|  | bool added = false; | 
|  | foreach (dgidx, dg; Handler) | 
|  | { | 
|  | // Handle normal function objects | 
|  | static if (isSomeFunction!dg) | 
|  | { | 
|  | alias Params = Parameters!dg; | 
|  | static if (Params.length == 0) | 
|  | { | 
|  | // Just check exception functions in the first | 
|  | // inner iteration (over delegates) | 
|  | if (tidx > 0) | 
|  | continue; | 
|  | else | 
|  | { | 
|  | if (result.exceptionFuncIdx != -1) | 
|  | assert(false, "duplicate parameter-less (error-)function specified"); | 
|  | result.exceptionFuncIdx = dgidx; | 
|  | } | 
|  | } | 
|  | else static if (is(Params[0] == T) || is(Unqual!(Params[0]) == T)) | 
|  | { | 
|  | if (added) | 
|  | assert(false, "duplicate overload specified for type '" ~ T.stringof ~ "'"); | 
|  |  | 
|  | added = true; | 
|  | result.indices[tidx] = dgidx; | 
|  | } | 
|  | } | 
|  | else static if (__traits(compiles, { static assert(isSomeFunction!(dg!T)); })) | 
|  | { | 
|  | assert(result.generalFuncIdx == -1 || | 
|  | result.generalFuncIdx == dgidx, | 
|  | "Only one generic visitor function is allowed"); | 
|  | result.generalFuncIdx = dgidx; | 
|  | } | 
|  | // Handle composite visitors with opCall overloads | 
|  | } | 
|  |  | 
|  | if (!added) | 
|  | result.indices[tidx] = -1; | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | enum HandlerOverloadMap = visitGetOverloadMap(); | 
|  |  | 
|  | if (!variant.hasValue) | 
|  | { | 
|  | // Call the exception function. The HandlerOverloadMap | 
|  | // will have its exceptionFuncIdx field set to value != -1 if an | 
|  | // exception function has been specified; otherwise we just through an exception. | 
|  | static if (HandlerOverloadMap.exceptionFuncIdx != -1) | 
|  | return Handler[ HandlerOverloadMap.exceptionFuncIdx ](); | 
|  | else | 
|  | throw new VariantException("variant must hold a value before being visited."); | 
|  | } | 
|  |  | 
|  | foreach (idx, T; AllowedTypes) | 
|  | { | 
|  | if (auto ptr = variant.peek!T) | 
|  | { | 
|  | enum dgIdx = HandlerOverloadMap.indices[idx]; | 
|  |  | 
|  | static if (dgIdx == -1) | 
|  | { | 
|  | static if (HandlerOverloadMap.generalFuncIdx >= 0) | 
|  | return Handler[HandlerOverloadMap.generalFuncIdx](*ptr); | 
|  | else static if (Strict) | 
|  | static assert(false, "overload for type '" ~ T.stringof ~ "' hasn't been specified"); | 
|  | else static if (HandlerOverloadMap.exceptionFuncIdx != -1) | 
|  | return Handler[HandlerOverloadMap.exceptionFuncIdx](); | 
|  | else | 
|  | throw new VariantException( | 
|  | "variant holds value of type '" | 
|  | ~ T.stringof ~ | 
|  | "' but no visitor has been provided" | 
|  | ); | 
|  | } | 
|  | else | 
|  | { | 
|  | return Handler[ dgIdx ](*ptr); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | assert(false); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=21253 | 
|  | @system unittest | 
|  | { | 
|  | static struct A { int n; } | 
|  | static struct B {        } | 
|  |  | 
|  | auto a = Algebraic!(A, B)(B()); | 
|  | assert(a.visit!( | 
|  | (B _) => 42, | 
|  | (a  ) => a.n | 
|  | ) == 42); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | // validate that visit can be called with a const type | 
|  | struct Foo { int depth; } | 
|  | struct Bar { int depth; } | 
|  | alias FooBar = Algebraic!(Foo, Bar); | 
|  |  | 
|  | int depth(in FooBar fb) { | 
|  | return fb.visit!((Foo foo) => foo.depth, | 
|  | (Bar bar) => bar.depth); | 
|  | } | 
|  |  | 
|  | FooBar fb = Foo(3); | 
|  | assert(depth(fb) == 3); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=16383 | 
|  | @system unittest | 
|  | { | 
|  | class Foo {this() immutable {}} | 
|  | alias V = Algebraic!(immutable Foo); | 
|  |  | 
|  | auto x = V(new immutable Foo).visit!( | 
|  | (immutable(Foo) _) => 3 | 
|  | ); | 
|  | assert(x == 3); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=5310 | 
|  | @system unittest | 
|  | { | 
|  | const Variant a; | 
|  | assert(a == a); | 
|  | Variant b; | 
|  | assert(a == b); | 
|  | assert(b == a); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | const Variant a = [2]; | 
|  | assert(a[0] == 2); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=10017 | 
|  | @system unittest | 
|  | { | 
|  | static struct S | 
|  | { | 
|  | ubyte[Variant.size + 1] s; | 
|  | } | 
|  |  | 
|  | Variant v1, v2; | 
|  | v1 = S(); // the payload is allocated on the heap | 
|  | v2 = v1;  // AssertError: target must be non-null | 
|  | assert(v1 == v2); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=7069 | 
|  | @system unittest | 
|  | { | 
|  | import std.exception : assertThrown; | 
|  | Variant v; | 
|  |  | 
|  | int i = 10; | 
|  | v = i; | 
|  | static foreach (qual; AliasSeq!(Alias, ConstOf)) | 
|  | { | 
|  | assert(v.get!(qual!int) == 10); | 
|  | assert(v.get!(qual!float) == 10.0f); | 
|  | } | 
|  | static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) | 
|  | { | 
|  | assertThrown!VariantException(v.get!(qual!int)); | 
|  | } | 
|  |  | 
|  | const(int) ci = 20; | 
|  | v = ci; | 
|  | static foreach (qual; AliasSeq!(ConstOf)) | 
|  | { | 
|  | assert(v.get!(qual!int) == 20); | 
|  | assert(v.get!(qual!float) == 20.0f); | 
|  | } | 
|  | static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf)) | 
|  | { | 
|  | assertThrown!VariantException(v.get!(qual!int)); | 
|  | assertThrown!VariantException(v.get!(qual!float)); | 
|  | } | 
|  |  | 
|  | immutable(int) ii = ci; | 
|  | v = ii; | 
|  | static foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf)) | 
|  | { | 
|  | assert(v.get!(qual!int) == 20); | 
|  | assert(v.get!(qual!float) == 20.0f); | 
|  | } | 
|  | static foreach (qual; AliasSeq!(Alias, SharedOf)) | 
|  | { | 
|  | assertThrown!VariantException(v.get!(qual!int)); | 
|  | assertThrown!VariantException(v.get!(qual!float)); | 
|  | } | 
|  |  | 
|  | int[] ai = [1,2,3]; | 
|  | v = ai; | 
|  | static foreach (qual; AliasSeq!(Alias, ConstOf)) | 
|  | { | 
|  | assert(v.get!(qual!(int[])) == [1,2,3]); | 
|  | assert(v.get!(qual!(int)[]) == [1,2,3]); | 
|  | } | 
|  | static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) | 
|  | { | 
|  | assertThrown!VariantException(v.get!(qual!(int[]))); | 
|  | assertThrown!VariantException(v.get!(qual!(int)[])); | 
|  | } | 
|  |  | 
|  | const(int[]) cai = [4,5,6]; | 
|  | v = cai; | 
|  | static foreach (qual; AliasSeq!(ConstOf)) | 
|  | { | 
|  | assert(v.get!(qual!(int[])) == [4,5,6]); | 
|  | assert(v.get!(qual!(int)[]) == [4,5,6]); | 
|  | } | 
|  | static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf)) | 
|  | { | 
|  | assertThrown!VariantException(v.get!(qual!(int[]))); | 
|  | assertThrown!VariantException(v.get!(qual!(int)[])); | 
|  | } | 
|  |  | 
|  | immutable(int[]) iai = [7,8,9]; | 
|  | v = iai; | 
|  | //assert(v.get!(immutable(int[])) == [7,8,9]);   // Bug ??? runtime error | 
|  | assert(v.get!(immutable(int)[]) == [7,8,9]); | 
|  | assert(v.get!(const(int[])) == [7,8,9]); | 
|  | assert(v.get!(const(int)[]) == [7,8,9]); | 
|  | //assert(v.get!(shared(const(int[]))) == cast(shared const)[7,8,9]);    // Bug ??? runtime error | 
|  | //assert(v.get!(shared(const(int))[]) == cast(shared const)[7,8,9]);    // Bug ??? runtime error | 
|  | static foreach (qual; AliasSeq!(Alias)) | 
|  | { | 
|  | assertThrown!VariantException(v.get!(qual!(int[]))); | 
|  | assertThrown!VariantException(v.get!(qual!(int)[])); | 
|  | } | 
|  |  | 
|  | class A {} | 
|  | class B : A {} | 
|  | B b = new B(); | 
|  | v = b; | 
|  | static foreach (qual; AliasSeq!(Alias, ConstOf)) | 
|  | { | 
|  | assert(v.get!(qual!B) is b); | 
|  | assert(v.get!(qual!A) is b); | 
|  | assert(v.get!(qual!Object) is b); | 
|  | } | 
|  | static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) | 
|  | { | 
|  | assertThrown!VariantException(v.get!(qual!B)); | 
|  | assertThrown!VariantException(v.get!(qual!A)); | 
|  | assertThrown!VariantException(v.get!(qual!Object)); | 
|  | } | 
|  |  | 
|  | const(B) cb = new B(); | 
|  | v = cb; | 
|  | static foreach (qual; AliasSeq!(ConstOf)) | 
|  | { | 
|  | assert(v.get!(qual!B) is cb); | 
|  | assert(v.get!(qual!A) is cb); | 
|  | assert(v.get!(qual!Object) is cb); | 
|  | } | 
|  | static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf)) | 
|  | { | 
|  | assertThrown!VariantException(v.get!(qual!B)); | 
|  | assertThrown!VariantException(v.get!(qual!A)); | 
|  | assertThrown!VariantException(v.get!(qual!Object)); | 
|  | } | 
|  |  | 
|  | immutable(B) ib = new immutable(B)(); | 
|  | v = ib; | 
|  | static foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf)) | 
|  | { | 
|  | assert(v.get!(qual!B) is ib); | 
|  | assert(v.get!(qual!A) is ib); | 
|  | assert(v.get!(qual!Object) is ib); | 
|  | } | 
|  | static foreach (qual; AliasSeq!(Alias, SharedOf)) | 
|  | { | 
|  | assertThrown!VariantException(v.get!(qual!B)); | 
|  | assertThrown!VariantException(v.get!(qual!A)); | 
|  | assertThrown!VariantException(v.get!(qual!Object)); | 
|  | } | 
|  |  | 
|  | shared(B) sb = new shared B(); | 
|  | v = sb; | 
|  | static foreach (qual; AliasSeq!(SharedOf, SharedConstOf)) | 
|  | { | 
|  | assert(v.get!(qual!B) is sb); | 
|  | assert(v.get!(qual!A) is sb); | 
|  | assert(v.get!(qual!Object) is sb); | 
|  | } | 
|  | static foreach (qual; AliasSeq!(Alias, ImmutableOf, ConstOf)) | 
|  | { | 
|  | assertThrown!VariantException(v.get!(qual!B)); | 
|  | assertThrown!VariantException(v.get!(qual!A)); | 
|  | assertThrown!VariantException(v.get!(qual!Object)); | 
|  | } | 
|  |  | 
|  | shared(const(B)) scb = new shared const B(); | 
|  | v = scb; | 
|  | static foreach (qual; AliasSeq!(SharedConstOf)) | 
|  | { | 
|  | assert(v.get!(qual!B) is scb); | 
|  | assert(v.get!(qual!A) is scb); | 
|  | assert(v.get!(qual!Object) is scb); | 
|  | } | 
|  | static foreach (qual; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) | 
|  | { | 
|  | assertThrown!VariantException(v.get!(qual!B)); | 
|  | assertThrown!VariantException(v.get!(qual!A)); | 
|  | assertThrown!VariantException(v.get!(qual!Object)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=12540 | 
|  | @system unittest | 
|  | { | 
|  | static struct DummyScope | 
|  | { | 
|  | alias Alias12540 = Algebraic!Class12540; | 
|  |  | 
|  | static class Class12540 | 
|  | { | 
|  | Alias12540 entity; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | // https://issues.dlang.org/show_bug.cgi?id=10194 | 
|  | // Also test for elaborate copying | 
|  | static struct S | 
|  | { | 
|  | @disable this(); | 
|  | this(int dummy) | 
|  | { | 
|  | ++cnt; | 
|  | } | 
|  |  | 
|  | this(this) | 
|  | { | 
|  | ++cnt; | 
|  | } | 
|  |  | 
|  | @disable S opAssign(); | 
|  |  | 
|  | ~this() | 
|  | { | 
|  | --cnt; | 
|  | assert(cnt >= 0); | 
|  | } | 
|  | static int cnt = 0; | 
|  | } | 
|  |  | 
|  | { | 
|  | Variant v; | 
|  | { | 
|  | v = S(0); | 
|  | assert(S.cnt == 1); | 
|  | } | 
|  | assert(S.cnt == 1); | 
|  |  | 
|  | // assigning a new value should destroy the existing one | 
|  | v = 0; | 
|  | assert(S.cnt == 0); | 
|  |  | 
|  | // destroying the variant should destroy it's current value | 
|  | v = S(0); | 
|  | assert(S.cnt == 1); | 
|  | } | 
|  | assert(S.cnt == 0); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | // https://issues.dlang.org/show_bug.cgi?id=13300 | 
|  | static struct S | 
|  | { | 
|  | this(this) {} | 
|  | ~this() {} | 
|  | } | 
|  |  | 
|  | static assert( hasElaborateCopyConstructor!(Variant)); | 
|  | static assert(!hasElaborateCopyConstructor!(Algebraic!bool)); | 
|  | static assert( hasElaborateCopyConstructor!(Algebraic!S)); | 
|  | static assert( hasElaborateCopyConstructor!(Algebraic!(bool, S))); | 
|  |  | 
|  | static assert( hasElaborateDestructor!(Variant)); | 
|  | static assert(!hasElaborateDestructor!(Algebraic!bool)); | 
|  | static assert( hasElaborateDestructor!(Algebraic!S)); | 
|  | static assert( hasElaborateDestructor!(Algebraic!(bool, S))); | 
|  |  | 
|  | import std.array; | 
|  | alias Value = Algebraic!bool; | 
|  |  | 
|  | static struct T | 
|  | { | 
|  | Value value; | 
|  | @disable this(); | 
|  | } | 
|  | auto a = appender!(T[]); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=13871 | 
|  | @system unittest | 
|  | { | 
|  | alias A = Algebraic!(int, typeof(null)); | 
|  | static struct B { A value; } | 
|  | alias C = std.variant.Algebraic!B; | 
|  |  | 
|  | C var; | 
|  | var = C(B()); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | import std.exception : assertThrown, assertNotThrown; | 
|  | // Make sure Variant can handle types with opDispatch but no length field. | 
|  | struct SWithNoLength | 
|  | { | 
|  | void opDispatch(string s)() { } | 
|  | } | 
|  |  | 
|  | struct SWithLength | 
|  | { | 
|  | @property int opDispatch(string s)() | 
|  | { | 
|  | // Assume that s == "length" | 
|  | return 5; // Any value is OK for test. | 
|  | } | 
|  | } | 
|  |  | 
|  | SWithNoLength sWithNoLength; | 
|  | Variant v = sWithNoLength; | 
|  | assertThrown!VariantException(v.length); | 
|  |  | 
|  | SWithLength sWithLength; | 
|  | v = sWithLength; | 
|  | assertNotThrown!VariantException(v.get!SWithLength.length); | 
|  | assertThrown!VariantException(v.length); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=13534 | 
|  | @system unittest | 
|  | { | 
|  | static assert(!__traits(compiles, () @safe { | 
|  | auto foo() @system { return 3; } | 
|  | auto v = Variant(&foo); | 
|  | v(); // foo is called in safe code!? | 
|  | })); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=15039 | 
|  | @system unittest | 
|  | { | 
|  | import std.typecons; | 
|  | import std.variant; | 
|  |  | 
|  | alias IntTypedef = Typedef!int; | 
|  | alias Obj = Algebraic!(int, IntTypedef, This[]); | 
|  |  | 
|  | Obj obj = 1; | 
|  |  | 
|  | obj.visit!( | 
|  | (int x) {}, | 
|  | (IntTypedef x) {}, | 
|  | (Obj[] x) {}, | 
|  | ); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=15791 | 
|  | @system unittest | 
|  | { | 
|  | int n = 3; | 
|  | struct NS1 { int foo() { return n + 10; } } | 
|  | struct NS2 { int foo() { return n * 10; } } | 
|  |  | 
|  | Variant v; | 
|  | v = NS1(); | 
|  | assert(v.get!NS1.foo() == 13); | 
|  | v = NS2(); | 
|  | assert(v.get!NS2.foo() == 30); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=15827 | 
|  | @system unittest | 
|  | { | 
|  | static struct Foo15827 { Variant v; this(Foo15827 v) {} } | 
|  | Variant v = Foo15827.init; | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=18934 | 
|  | @system unittest | 
|  | { | 
|  | static struct S | 
|  | { | 
|  | const int x; | 
|  | } | 
|  |  | 
|  | auto s = S(42); | 
|  | Variant v = s; | 
|  | auto s2 = v.get!S; | 
|  | assert(s2.x == 42); | 
|  | Variant v2 = v; // support copying from one variant to the other | 
|  | v2 = S(2); | 
|  | v = v2; | 
|  | assert(v.get!S.x == 2); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=19200 | 
|  | @system unittest | 
|  | { | 
|  | static struct S | 
|  | { | 
|  | static int opBinaryRight(string op : "|", T)(T rhs) | 
|  | { | 
|  | return 3; | 
|  | } | 
|  | } | 
|  |  | 
|  | S s; | 
|  | Variant v; | 
|  | auto b = v | s; | 
|  | assert(b == 3); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=11061 | 
|  | @system unittest | 
|  | { | 
|  | int[4] el = [0, 1, 2, 3]; | 
|  | int[3] nl = [0, 1, 2]; | 
|  | Variant v1 = el; | 
|  | assert(v1 == el); // Compare Var(static) to static | 
|  | assert(v1 != nl); // Compare static arrays of different length | 
|  | assert(v1 == [0, 1, 2, 3]); // Compare Var(static) to dynamic. | 
|  | assert(v1 != [0, 1, 2]); | 
|  | int[] dyn = [0, 1, 2, 3]; | 
|  | v1 = dyn; | 
|  | assert(v1 == el); // Compare Var(dynamic) to static. | 
|  | assert(v1 == [0, 1] ~ [2, 3]); // Compare Var(dynamic) to dynamic | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=15940 | 
|  | @system unittest | 
|  | { | 
|  | class C { } | 
|  | struct S | 
|  | { | 
|  | C a; | 
|  | alias a this; | 
|  | } | 
|  | S s = S(new C()); | 
|  | auto v = Variant(s); // compile error | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | // Test if we don't have scoping issues. | 
|  | Variant createVariant(int[] input) | 
|  | { | 
|  | int[2] el = [input[0], input[1]]; | 
|  | Variant v = el; | 
|  | return v; | 
|  | } | 
|  | Variant v = createVariant([0, 1]); | 
|  | createVariant([2, 3]); | 
|  | assert(v == [0,1]); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=19994 | 
|  | @safe unittest | 
|  | { | 
|  | alias Inner = Algebraic!(This*); | 
|  | alias Outer = Algebraic!(Inner, This*); | 
|  |  | 
|  | static assert(is(Outer.AllowedTypes == AliasSeq!(Inner, Outer*))); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=21296 | 
|  | @system unittest | 
|  | { | 
|  | immutable aa = ["0": 0]; | 
|  | auto v = Variant(aa); // compile error | 
|  | } |