1 // Written in the D programming language.
4 This module implements a
5 $(HTTP erdani.org/publications/cuj-04-2002.php.html,discriminated union)
7 $(HTTP en.wikipedia.org/wiki/Tagged_union,tagged union),
8 $(HTTP en.wikipedia.org/wiki/Algebraic_data_type,algebraic type)).
10 for type-uniform binary interfaces, interfacing with scripting
11 languages, and comfortable exploratory programming.
13 A $(LREF Variant) object can hold a value of any type, with very few
14 restrictions (such as `shared` types and noncopyable types). Setting the value
15 is as immediate as assigning to the `Variant` object. To read back the value of
16 the appropriate type `T`, use the $(LREF get) method. To query whether a
17 `Variant` currently holds a value of type `T`, use $(LREF peek). To fetch the
18 exact type currently held, call $(LREF type), which returns the `TypeInfo` of
21 In addition to $(LREF Variant), this module also defines the $(LREF Algebraic)
22 type constructor. Unlike `Variant`, `Algebraic` only allows a finite set of
23 types, which are specified in the instantiation (e.g. $(D Algebraic!(int,
24 string)) may only hold an `int` or a `string`).
26 $(RED Warning: $(LREF Algebraic) is outdated and not recommended for use in new
27 code. Instead, use $(REF SumType, std,sumtype).)
29 Credits: Reviewed by Brad Roberts. Daniel Keep provided a detailed code review
30 prompting the following improvements: (1) better support for arrays; (2) support
31 for associative arrays; (3) friendlier behavior towards the garbage collector.
32 Copyright: Copyright Andrei Alexandrescu 2007 - 2015.
33 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
34 Authors: $(HTTP erdani.org, Andrei Alexandrescu)
35 Source: $(PHOBOSSRC std/variant.d)
39 import std
.meta
, std
.traits
, std
.typecons
;
44 Variant a
; // Must assign before use, otherwise exception ensues
45 // Initialize with an integer; make the type int
47 assert(b
.type
== typeid(int));
49 assert(b
.peek
!(int) !is null && *b
.peek
!(int) == 42);
50 // Automatically convert per language rules
51 auto x
= b
.get
!(real);
53 // Assign any other type, including other variants
56 assert(a
.type
== typeid(double));
57 // Implicit conversions work just as with built-in types
59 // Check for convertibility
60 assert(!a
.convertsTo
!(int)); // double not convertible to int
61 // Strings and all other arrays are supported
62 a
= "now I'm a string";
63 assert(a
== "now I'm a string");
65 // can also assign arrays
67 assert(a
.length
== 42);
71 // Can also assign class values
75 assert(*a
.peek
!(Foo
) == foo
); // and full type information is preserved
79 Gives the `sizeof` the largest type given.
81 See_Also: $(LINK https://forum.dlang.org/thread/wbpnncxepehgcswhuazl@forum.dlang.org?page=1)
83 template maxSize(Ts
...)
87 static foreach (i
, T
; Ts
)
89 static if (!is(T
== void))
90 mixin("T _field_", i
, ";");
93 enum maxSize
= Impl
.sizeof
;
99 struct Cat
{ int a
, b
, c
; }
113 static assert(maxSize
!(int, long) == 8);
114 static assert(maxSize
!(bool, byte) == 1);
115 static assert(maxSize
!(bool, Cat
) == 12);
116 static assert(maxSize
!(char) == 1);
117 static assert(maxSize
!(char, short, ubyte) == 2);
118 static assert(maxSize
!(char, long, ubyte) == 8);
119 import std
.algorithm
.comparison
: max
;
120 static assert(maxSize
!(long, S
) == max(long.sizeof
, S
.sizeof
));
121 static assert(maxSize
!(S
, T
) == max(S
.sizeof
, T
.sizeof
));
122 static assert(maxSize
!(int, ubyte[7]) == 7);
123 static assert(maxSize
!(int, ubyte[3]) == 4);
124 static assert(maxSize
!(int, int, ubyte[3]) == 4);
125 static assert(maxSize
!(void, int, ubyte[3]) == 4);
126 static assert(maxSize
!(void) == 1);
131 private alias This2Variant(V
, T
...) = AliasSeq
!(ReplaceTypeUnless
!(isAlgebraic
, This
, V
, T
));
133 // We can't just use maxAlignment because no types might be specified
134 // to VariantN, so handle that here and then pass along the rest.
135 private template maxVariantAlignment(U
...)
138 static if (U
.length
== 0)
140 import std
.algorithm
.comparison
: max
;
141 enum maxVariantAlignment
= max(real.alignof
, size_t
.alignof
);
144 enum maxVariantAlignment
= maxAlignment
!(U
);
148 * Back-end type seldom used directly by user
149 * code. Two commonly-used types using `VariantN` are:
151 * $(OL $(LI $(LREF Algebraic): A closed discriminated union with a
152 * limited type universe (e.g., $(D Algebraic!(int, double,
153 * string)) only accepts these three types and rejects anything
154 * else).) $(LI $(LREF Variant): An open discriminated union allowing an
155 * unbounded set of types. If any of the types in the `Variant`
156 * are larger than the largest built-in type, they will automatically
157 * be boxed. This means that even large types will only be the size
158 * of a pointer within the `Variant`, but this also implies some
159 * overhead. `Variant` can accommodate all primitive types and
160 * all user-defined types.))
162 * Both `Algebraic` and `Variant` share $(D
163 * VariantN)'s interface. (See their respective documentations below.)
165 * `VariantN` is a discriminated union type parameterized
166 * with the largest size of the types stored (`maxDataSize`)
167 * and with the list of allowed types (`AllowedTypes`). If
168 * the list is empty, then any type up of size up to $(D
169 * maxDataSize) (rounded up for alignment) can be stored in a
170 * `VariantN` object without being boxed (types larger
171 * than this will be boxed).
174 struct VariantN(size_t maxDataSize
, AllowedTypesParam
...)
177 The list of allowed types. If empty, any type is allowed.
179 alias AllowedTypes
= This2Variant
!(VariantN
, AllowedTypesParam
);
182 // Compute the largest practical size from maxDataSize
186 ubyte[maxDataSize
] data
;
188 enum size
= SizeChecker
.sizeof
- (int function()).sizeof
;
190 /** Tells whether a type `T` is statically _allowed for
191 * storage inside a `VariantN` object by looking
192 * `T` up in `AllowedTypes`.
194 public template allowed(T
)
199 //T.sizeof <= size &&
200 (AllowedTypes
.length
== 0 || staticIndexOf
!(T
, AllowedTypes
) >= 0);
203 // Each internal operation is encoded with an identifier. See
204 // the "handler" function below.
205 enum OpID
{ getTypeInfo
, get
, compare
, equals
, testConversion
, toString
,
206 index
, indexAssign
, catAssign
, copyOut
, length
,
207 apply
, postblit
, destruct
}
212 align(maxVariantAlignment
!(AllowedTypes
)) ubyte[size
] store
;
213 // conservatively mark the region as pointers
214 static if (size
>= (void*).sizeof
)
215 void*[size
/ (void*).sizeof
] p
;
217 ptrdiff_t
function(OpID selector
, ubyte[size
]* store
, void* data
) fptr
221 // Handler for an uninitialized value
222 static ptrdiff_t
handler(A
: void)(OpID selector
, ubyte[size
]*, void* parm
)
226 case OpID
.getTypeInfo
:
227 *cast(TypeInfo
*) parm
= typeid(A
);
230 auto target
= cast(VariantN
*) parm
;
231 target
.fptr
= &handler
!(A
);
232 // no need to copy the data (it's garbage)
236 auto rhs
= cast(const VariantN
*) parm
;
238 ?
0 // all uninitialized are equal
239 : ptrdiff_t
.min
; // uninitialized variant is not comparable otherwise
241 string
* target
= cast(string
*) parm
;
242 *target
= "<Uninitialized VariantN>";
248 case OpID
.testConversion
:
250 case OpID
.indexAssign
:
253 throw new VariantException(
254 "Attempt to use an uninitialized VariantN");
255 default: assert(false, "Invalid OpID");
260 // Handler for all of a type's operations
261 static ptrdiff_t
handler(A
)(OpID selector
, ubyte[size
]* pStore
, void* parm
)
263 import std
.conv
: to
;
264 static A
* getPtr(void* untyped
)
268 static if (A
.sizeof
<= size
)
269 return cast(A
*) untyped
;
271 return *cast(A
**) untyped
;
276 static ptrdiff_t
compare(A
* rhsPA
, A
* zis
, OpID selector
)
278 static if (is(typeof(*rhsPA
== *zis
)))
280 enum isEmptyStructWithoutOpEquals
= is(A
== struct) && A
.tupleof
.length
== 0 &&
281 !__traits(hasMember
, A
, "opEquals");
282 static if (isEmptyStructWithoutOpEquals
)
284 // The check above will always succeed if A is an empty struct.
285 // Don't generate unreachable code as seen in
286 // https://issues.dlang.org/show_bug.cgi?id=21231
293 static if (is(typeof(*zis
< *rhsPA
)))
295 // Many types (such as any using the default Object opCmp)
296 // will throw on an invalid opCmp, so do it only
297 // if the caller requests it.
298 if (selector
== OpID
.compare
)
299 return *zis
< *rhsPA ?
-1 : 1;
301 return ptrdiff_t
.min
;
305 // Not equal, and type does not support ordering
307 return ptrdiff_t
.min
;
313 // Type does not support comparisons at all.
314 return ptrdiff_t
.min
;
318 auto zis
= getPtr(pStore
);
319 // Input: TypeInfo object
320 // Output: target points to a copy of *me, if me was not null
321 // Returns: true iff the A can be converted to the type represented
322 // by the incoming TypeInfo
323 static bool tryPutting(A
* src
, TypeInfo targetType
, void* target
)
326 static if (isStaticArray
!A
&& is(typeof(UA
.init
[0])))
328 alias MutaTypes
= AliasSeq
!(UA
, typeof(UA
.init
[0])[], AllImplicitConversionTargets
!UA
);
332 alias MutaTypes
= AliasSeq
!(UA
, AllImplicitConversionTargets
!UA
);
334 alias ConstTypes
= staticMap
!(ConstOf
, MutaTypes
);
335 alias SharedTypes
= staticMap
!(SharedOf
, MutaTypes
);
336 alias SharedConstTypes
= staticMap
!(SharedConstOf
, MutaTypes
);
337 alias ImmuTypes
= staticMap
!(ImmutableOf
, MutaTypes
);
339 static if (is(A
== immutable))
340 alias AllTypes
= AliasSeq
!(ImmuTypes
, ConstTypes
, SharedConstTypes
);
341 else static if (is(A
== shared))
343 static if (is(A
== const))
344 alias AllTypes
= SharedConstTypes
;
346 alias AllTypes
= AliasSeq
!(SharedTypes
, SharedConstTypes
);
350 static if (is(A
== const))
351 alias AllTypes
= ConstTypes
;
353 alias AllTypes
= AliasSeq
!(MutaTypes
, ConstTypes
);
356 foreach (T
; AllTypes
)
358 if (targetType
!= typeid(T
))
361 // SPECIAL NOTE: variant only will ever create a new value with
362 // tryPutting (effectively), and T is ALWAYS the same type of
363 // A, but with different modifiers (and a limited set of
364 // implicit targets). So this checks to see if we can construct
365 // a T from A, knowing that prerequisite. This handles issues
366 // where the type contains some constant data aside from the
367 // modifiers on the type itself.
368 static if (is(typeof(delegate T() {return *src
;})) ||
369 is(T
== const(U
), U
) ||
370 is(T
== shared(U
), U
) ||
371 is(T
== shared const(U
), U
) ||
372 is(T
== immutable(U
), U
))
374 import core
.internal
.lifetime
: emplaceRef
;
376 auto zat
= cast(T
*) target
;
379 static if (T
.sizeof
> 0)
380 assert(target
, "target must be non-null");
382 static if (isStaticArray
!A
&& isDynamicArray
!T
)
384 auto this_
= (*src
)[];
385 emplaceRef(*cast(Unqual
!T
*) zat
, cast() cast(T
) this_
);
389 emplaceRef(*cast(Unqual
!T
*) zat
, *cast(UA
*) src
);
395 // type T is not constructible from A
397 assert(false, A
.stringof
);
406 case OpID
.getTypeInfo
:
407 *cast(TypeInfo
*) parm
= typeid(A
);
410 auto target
= cast(VariantN
*) parm
;
413 static if (target
.size
< A
.sizeof
)
415 if (target
.type
.tsize
< A
.sizeof
)
417 static if (is(A
== U
[n
], U
, size_t n
))
419 A
* p
= cast(A
*)(new U
[n
]).ptr
;
425 *cast(A
**)&target
.store
= p
;
428 tryPutting(zis
, typeid(A
), cast(void*) getPtr(&target
.store
))
430 target
.fptr
= &handler
!(A
);
433 auto t
= * cast(Tuple
!(TypeInfo
, void*)*) parm
;
434 return !tryPutting(zis
, t
[0], t
[1]);
435 case OpID
.testConversion
:
436 return !tryPutting(null, *cast(TypeInfo
*) parm
, null);
439 auto rhsP
= cast(VariantN
*) parm
;
440 auto rhsType
= rhsP
.type
;
442 if (rhsType
== typeid(A
))
445 auto rhsPA
= getPtr(&rhsP
.store
);
446 return compare(rhsPA
, zis
, selector
);
448 else if (rhsType
== typeid(void))
450 // No support for ordering comparisons with
451 // uninitialized vars
452 return ptrdiff_t
.min
;
455 // Do I convert to rhs?
456 if (tryPutting(zis
, rhsType
, &temp
.store
))
458 // cool, I do; temp's store contains my data in rhs's type!
459 // also fix up its fptr
460 temp
.fptr
= rhsP
.fptr
;
461 // now lhsWithRhsType is a full-blown VariantN of rhs's type
462 if (selector
== OpID
.compare
)
463 return temp
.opCmp(*rhsP
);
465 return temp
.opEquals(*rhsP
) ?
0 : 1;
467 // Does rhs convert to zis?
468 auto t
= tuple(typeid(A
), &temp
.store
);
469 if (rhsP
.fptr(OpID
.get
, &rhsP
.store
, &t
) == 0)
471 // cool! Now temp has rhs in my type!
472 auto rhsPA
= getPtr(&temp
.store
);
473 return compare(rhsPA
, zis
, selector
);
475 // Generate the function below only if the Variant's type is
476 // comparable with 'null'
477 static if (__traits(compiles
, () => A
.init
== null))
479 if (rhsType
== typeid(null))
481 // if rhsType is typeof(null), then we're comparing with 'null'
482 // this takes into account 'opEquals' and 'opCmp'
483 // all types that can compare with null have to following properties:
484 // if it's 'null' then it's equal to null, otherwise it's always greater
486 return *zis
== null ?
0 : 1;
489 return ptrdiff_t
.min
; // dunno
491 auto target
= cast(string
*) parm
;
492 static if (is(typeof(to
!(string
)(*zis
))))
494 *target
= to
!(string
)(*zis
);
497 // TODO: The following test evaluates to true for shared objects.
498 // Use __traits for now until this is sorted out.
499 // else static if (is(typeof((*zis).toString)))
500 else static if (__traits(compiles
, {(*zis
).toString();}))
502 *target
= (*zis
).toString();
507 throw new VariantException(typeid(A
), typeid(string
));
511 auto result
= cast(Variant
*) parm
;
512 static if (isArray
!(A
) && !is(immutable typeof(A
.init
[0]) == immutable void))
514 // array type; input and output are the same VariantN
515 size_t index
= result
.convertsTo
!(int)
516 ? result
.get
!(int) : result
.get
!(size_t
);
517 *result
= (*zis
)[index
];
520 else static if (isAssociativeArray
!(A
))
522 *result
= (*zis
)[result
.get
!(typeof(A
.init
.keys
[0]))];
527 throw new VariantException(typeid(A
), result
[0].type
);
530 case OpID
.indexAssign
:
531 // array type; result comes first, index comes second
532 auto args
= cast(Variant
*) parm
;
533 static if (isArray
!(A
) && is(typeof((*zis
)[0] = (*zis
)[0])))
535 size_t index
= args
[1].convertsTo
!(int)
536 ? args
[1].get
!(int) : args
[1].get
!(size_t
);
537 (*zis
)[index
] = args
[0].get
!(typeof((*zis
)[0]));
540 else static if (isAssociativeArray
!(A
) && is(typeof((*zis
)[A
.init
.keys
[0]] = A
.init
.values
[0])))
542 (*zis
)[args
[1].get
!(typeof(A
.init
.keys
[0]))]
543 = args
[0].get
!(typeof(A
.init
.values
[0]));
548 throw new VariantException(typeid(A
), args
[0].type
);
552 static if (!is(immutable typeof((*zis
)[0]) == immutable void) &&
553 is(typeof((*zis
)[0])) && is(typeof(*zis
~= *zis
)))
555 // array type; parm is the element to append
556 auto arg
= cast(Variant
*) parm
;
557 alias E
= typeof((*zis
)[0]);
558 if (arg
[0].convertsTo
!(E
))
560 // append one element to the array
561 (*zis
) ~= [ arg
[0].get
!(E
) ];
565 // append a whole array to the array
566 (*zis
) ~= arg
[0].get
!(A
);
572 throw new VariantException(typeid(A
), typeid(void[]));
576 static if (isArray
!(A
) || isAssociativeArray
!(A
))
582 throw new VariantException(typeid(A
), typeid(void[]));
586 static if (!isFunctionPointer
!A
&& !isDelegate
!A
)
588 import std
.conv
: text
;
589 import std
.exception
: enforce
;
590 enforce(0, text("Cannot apply `()' to a value of type `",
595 import std
.conv
: text
;
596 import std
.exception
: enforce
;
597 alias ParamTypes
= Parameters
!A
;
598 auto p
= cast(Variant
*) parm
;
599 auto argCount
= p
.get
!size_t
;
600 // To assign the tuple we need to use the unqualified version,
601 // otherwise we run into issues such as with const values.
602 // We still get the actual type from the Variant though
603 // to ensure that we retain const correctness.
604 Tuple
!(staticMap
!(Unqual
, ParamTypes
)) t
;
605 enforce(t
.length
== argCount
,
606 text("Argument count mismatch: ",
607 A
.stringof
, " expects ", t
.length
,
608 " argument(s), not ", argCount
, "."));
609 auto variantArgs
= p
[1 .. argCount
+ 1];
610 foreach (i
, T
; ParamTypes
)
612 t
[i
] = cast() variantArgs
[i
].get
!T
;
615 auto args
= cast(Tuple
!(ParamTypes
))t
;
616 static if (is(ReturnType
!A
== void))
619 *p
= Variant
.init
; // void returns uninitialized Variant.
623 *p
= (*zis
)(args
.expand
);
629 static if (hasElaborateCopyConstructor
!A
)
636 static if (hasElaborateDestructor
!A
)
642 default: assert(false);
648 /** Constructs a `VariantN` value given an argument of a
649 * generic type. Statically rejects disallowed types.
654 static assert(allowed
!(T
), "Cannot store a " ~ T
.stringof
655 ~ " in a " ~ VariantN
.stringof
);
659 /// Allows assignment from a subset algebraic type
660 this(T
: VariantN
!(tsize
, Types
), size_t tsize
, Types
...)(T value
)
661 if (!is(T
: VariantN
) && Types
.length
> 0 && allSatisfy
!(allowed
, Types
))
666 static if (!AllowedTypes
.length || anySatisfy
!(hasElaborateCopyConstructor
, AllowedTypes
))
670 fptr(OpID
.postblit
, &store
, null);
674 static if (!AllowedTypes
.length || anySatisfy
!(hasElaborateDestructor
, AllowedTypes
))
678 // Infer the safety of the provided types
679 static if (AllowedTypes
.length
)
686 (() @trusted => fptr(OpID
.destruct
, &store
, null))();
690 /** Assigns a `VariantN` from a generic
691 * argument. Statically rejects disallowed types. */
693 VariantN
opAssign(T
)(T rhs
)
695 static assert(allowed
!(T
), "Cannot store a " ~ T
.stringof
696 ~ " in a " ~ VariantN
.stringof
~ ". Valid types are "
697 ~ AllowedTypes
.stringof
);
699 static if (is(T
: VariantN
))
701 rhs
.fptr(OpID
.copyOut
, &rhs
.store
, &this);
703 else static if (is(T
: const(VariantN
)))
706 "Assigning Variant objects from const Variant"~
707 " objects is currently not supported.");
711 import core
.lifetime
: copyEmplace
;
713 static if (!AllowedTypes
.length || anySatisfy
!(hasElaborateDestructor
, AllowedTypes
))
715 // Assignment should destruct previous value
716 fptr(OpID
.destruct
, &store
, null);
719 static if (T
.sizeof
<= size
)
720 copyEmplace(rhs
, *cast(T
*) &store
);
723 static if (is(T
== U
[n
], U
, size_t n
))
724 auto p
= cast(T
*) (new U
[n
]).ptr
;
727 copyEmplace(rhs
, *p
);
728 *(cast(T
**) &store
) = p
;
736 // Allow assignment from another variant which is a subset of this one
737 VariantN
opAssign(T
: VariantN
!(tsize
, Types
), size_t tsize
, Types
...)(T rhs
)
738 if (!is(T
: VariantN
) && Types
.length
> 0 && allSatisfy
!(allowed
, Types
))
740 // discover which type rhs is actually storing
741 foreach (V
; T
.AllowedTypes
)
742 if (rhs
.type
== typeid(V
))
743 return this = rhs
.get
!V
;
744 assert(0, T
.AllowedTypes
.stringof
);
748 Variant
opCall(P
...)(auto ref P params
)
750 Variant
[P
.length
+ 1] pack
;
752 foreach (i
, _
; params
)
754 pack
[i
+ 1] = params
[i
];
756 fptr(OpID
.apply
, &store
, &pack
);
760 /** Returns true if and only if the `VariantN` object
761 * holds a valid value (has been initialized with, or assigned
762 * from, a valid value).
764 @property bool hasValue() const pure nothrow
766 // @@@BUG@@@ in compiler, the cast shouldn't be needed
767 return cast(typeof(&handler
!(void))) fptr
!= &handler
!(void);
778 assert(!a
.hasValue
); // still no value
784 * If the `VariantN` object holds a value of the
785 * $(I exact) type `T`, returns a pointer to that
786 * value. Otherwise, returns `null`. In cases
787 * where `T` is statically disallowed, $(D
788 * peek) will not compile.
790 @property inout(T
)* peek(T
)() inout
792 static if (!is(T
== void))
793 static assert(allowed
!(T
), "Cannot store a " ~ T
.stringof
794 ~ " in a " ~ VariantN
.stringof
);
795 if (type
!= typeid(T
))
797 static if (T
.sizeof
<= size
)
798 return cast(inout T
*)&store
;
800 return *cast(inout T
**)&store
;
808 auto b
= a
.peek
!(int);
815 * Returns the `typeid` of the currently held value.
818 @property TypeInfo
type() const nothrow @trusted
820 scope(failure
) assert(0);
823 fptr(OpID
.getTypeInfo
, null, &result
);
828 * Returns `true` if and only if the `VariantN`
829 * object holds an object implicitly convertible to type `T`.
830 * Implicit convertibility is defined as per
831 * $(REF_ALTTEXT AllImplicitConversionTargets, AllImplicitConversionTargets, std,traits).
834 @property bool convertsTo(T
)() const
836 TypeInfo info
= typeid(T
);
837 return fptr(OpID
.testConversion
, null, &info
) == 0;
841 Returns the value stored in the `VariantN` object, either by specifying the
842 needed type or the index in the list of allowed types. The latter overload
843 only applies to bounded variants (e.g. $(LREF Algebraic)).
846 T = The requested type. The currently stored value must implicitly convert
847 to the requested type, in fact `DecayStaticToDynamicArray!T`. If an
848 implicit conversion is not possible, throws a `VariantException`.
849 index = The index of the type among `AllowedTypesParam`, zero-based.
851 @property inout(T
) get(T
)() inout
853 inout(T
) result
= void;
854 static if (is(T
== shared))
855 alias R
= shared Unqual
!T
;
858 auto buf
= tuple(typeid(T
), cast(R
*)&result
);
860 if (fptr(OpID
.get
, cast(ubyte[size
]*) &store
, &buf
))
862 throw new VariantException(type
, typeid(T
));
868 @property auto get(uint index
)() inout
869 if (index
< AllowedTypes
.length
)
871 foreach (i
, T
; AllowedTypes
)
873 static if (index
== i
) return get
!T
;
879 * Returns the value stored in the `VariantN` object,
880 * explicitly converted (coerced) to the requested type $(D
881 * T). If `T` is a string type, the value is formatted as
882 * a string. If the `VariantN` object is a string, a
883 * parse of the string to type `T` is attempted. If a
884 * conversion is not possible, throws a $(D
888 @property T
coerce(T
)()
890 import std
.conv
: to
, text
;
891 static if (isNumeric
!T || isBoolean
!T
)
895 // maybe optimize this fella; handle ints separately
896 return to
!T(get
!real);
898 else if (convertsTo
!(const(char)[]))
900 return to
!T(get
!(const(char)[]));
902 // I'm not sure why this doesn't convert to const(char),
903 // but apparently it doesn't (probably a deeper bug).
905 // Until that is fixed, this quick addition keeps a common
906 // function working. "10".coerce!int ought to work.
907 else if (convertsTo
!(immutable(char)[]))
909 return to
!T(get
!(immutable(char)[]));
913 import std
.exception
: enforce
;
914 enforce(false, text("Type ", type
, " does not convert to ",
919 else static if (is(T
: Object
))
921 return to
!(T
)(get
!(Object
));
923 else static if (isSomeString
!(T
))
925 return to
!(T
)(toString());
930 static assert(false, "unsupported type for coercion");
935 * Formats the stored value as a string.
941 fptr(OpID
.toString
, &store
, &result
) == 0 ||
assert(false);
946 * Comparison for equality used by the "==" and "!=" operators.
949 // returns 1 if the two are equal
950 bool opEquals(T
)(auto ref T rhs
) const
951 if (allowed
!T ||
is(immutable T
== immutable VariantN
))
953 static if (is(immutable T
== immutable VariantN
))
956 auto temp
= VariantN(rhs
);
957 return !fptr(OpID
.equals
, cast(ubyte[size
]*) &store
,
961 // workaround for bug 10567 fix
962 int opCmp(ref const VariantN rhs
) const
964 return (cast() this).opCmp
!(VariantN
)(cast() rhs
);
968 * Ordering comparison used by the "<", "<=", ">", and ">="
969 * operators. In case comparison is not sensible between the held
970 * value and `rhs`, an exception is thrown.
974 if (allowed
!T
) // includes T == VariantN
976 static if (is(T
== VariantN
))
979 auto temp
= VariantN(rhs
);
980 auto result
= fptr(OpID
.compare
, &store
, &temp
);
981 if (result
== ptrdiff_t
.min
)
983 throw new VariantException(type
, temp
.type
);
986 assert(result
>= -1 && result
<= 1); // Should be true for opCmp.
987 return cast(int) result
;
991 * Computes the hash of the held value.
994 size_t
toHash() const nothrow @safe
996 return type
.getHash(&store
);
999 private VariantN
opArithmetic(T
, string op
)(T other
)
1001 static if (isInstanceOf
!(.VariantN
, T
))
1003 string
tryUseType(string tp
)
1005 import std
.format
: format
;
1007 static if (allowed
!%1$s
&& T
.allowed
!%1$s
)
1008 if (convertsTo
!%1$s
&& other
.convertsTo
!%1$s
)
1009 return VariantN(get
!%1$s %2$s other
.get
!%1$s
);
1013 mixin(tryUseType("uint"));
1014 mixin(tryUseType("int"));
1015 mixin(tryUseType("ulong"));
1016 mixin(tryUseType("long"));
1017 mixin(tryUseType("float"));
1018 mixin(tryUseType("double"));
1019 mixin(tryUseType("real"));
1023 static if (allowed
!T
)
1024 if (auto pv
= peek
!T
) return VariantN(mixin("*pv " ~ op
~ " other"));
1025 static if (allowed
!uint && is(typeof(T
.max
) : uint) && isUnsigned
!T
)
1026 if (convertsTo
!uint) return VariantN(mixin("get!(uint) " ~ op
~ " other"));
1027 static if (allowed
!int && is(typeof(T
.max
) : int) && !isUnsigned
!T
)
1028 if (convertsTo
!int) return VariantN(mixin("get!(int) " ~ op
~ " other"));
1029 static if (allowed
!ulong && is(typeof(T
.max
) : ulong) && isUnsigned
!T
)
1030 if (convertsTo
!ulong) return VariantN(mixin("get!(ulong) " ~ op
~ " other"));
1031 static if (allowed
!long && is(typeof(T
.max
) : long) && !isUnsigned
!T
)
1032 if (convertsTo
!long) return VariantN(mixin("get!(long) " ~ op
~ " other"));
1033 static if (allowed
!float && is(T
: float))
1034 if (convertsTo
!float) return VariantN(mixin("get!(float) " ~ op
~ " other"));
1035 static if (allowed
!double && is(T
: double))
1036 if (convertsTo
!double) return VariantN(mixin("get!(double) " ~ op
~ " other"));
1037 static if (allowed
!real && is (T
: real))
1038 if (convertsTo
!real) return VariantN(mixin("get!(real) " ~ op
~ " other"));
1041 throw new VariantException("No possible match found for VariantN "~op
~" "~T
.stringof
);
1044 private VariantN
opLogic(T
, string op
)(T other
)
1047 static if (is(T
== VariantN
))
1049 if (convertsTo
!(uint) && other
.convertsTo
!(uint))
1050 result
= mixin("get!(uint) " ~ op
~ " other.get!(uint)");
1051 else if (convertsTo
!(int) && other
.convertsTo
!(int))
1052 result
= mixin("get!(int) " ~ op
~ " other.get!(int)");
1053 else if (convertsTo
!(ulong) && other
.convertsTo
!(ulong))
1054 result
= mixin("get!(ulong) " ~ op
~ " other.get!(ulong)");
1056 result
= mixin("get!(long) " ~ op
~ " other.get!(long)");
1060 if (is(typeof(T
.max
) : uint) && T
.min
== 0 && convertsTo
!(uint))
1061 result
= mixin("get!(uint) " ~ op
~ " other");
1062 else if (is(typeof(T
.max
) : int) && T
.min
< 0 && convertsTo
!(int))
1063 result
= mixin("get!(int) " ~ op
~ " other");
1064 else if (is(typeof(T
.max
) : ulong) && T
.min
== 0
1065 && convertsTo
!(ulong))
1066 result
= mixin("get!(ulong) " ~ op
~ " other");
1068 result
= mixin("get!(long) " ~ op
~ " other");
1074 * Arithmetic between `VariantN` objects and numeric
1075 * values. All arithmetic operations return a `VariantN`
1076 * object typed depending on the types of both values
1077 * involved. The conversion rules mimic D's built-in rules for
1078 * arithmetic conversions.
1080 VariantN
opBinary(string op
, T
)(T rhs
)
1081 if ((op
== "+" || op
== "-" || op
== "*" || op
== "/" || op
== "^^" || op
== "%") &&
1082 is(typeof(opArithmetic
!(T
, op
)(rhs
))))
1083 { return opArithmetic
!(T
, op
)(rhs
); }
1085 VariantN
opBinary(string op
, T
)(T rhs
)
1086 if ((op
== "&" || op
== "|" || op
== "^" || op
== ">>" || op
== "<<" || op
== ">>>") &&
1087 is(typeof(opLogic
!(T
, op
)(rhs
))))
1088 { return opLogic
!(T
, op
)(rhs
); }
1090 VariantN
opBinaryRight(string op
, T
)(T lhs
)
1091 if ((op
== "+" || op
== "*") &&
1092 is(typeof(opArithmetic
!(T
, op
)(lhs
))))
1093 { return opArithmetic
!(T
, op
)(lhs
); }
1095 VariantN
opBinaryRight(string op
, T
)(T lhs
)
1096 if ((op
== "&" || op
== "|" || op
== "^") &&
1097 is(typeof(opLogic
!(T
, op
)(lhs
))))
1098 { return opLogic
!(T
, op
)(lhs
); }
1100 VariantN
opBinary(string op
, T
)(T rhs
)
1108 // VariantN opBinaryRight(string op, T)(T rhs)
1111 // VariantN temp = rhs;
1117 VariantN
opOpAssign(string op
, T
)(T rhs
)
1119 static if (op
!= "~")
1121 mixin("return this = this" ~ op
~ "rhs;");
1125 auto toAppend
= Variant(rhs
);
1126 fptr(OpID
.catAssign
, &store
, &toAppend
) == 0 ||
assert(false);
1132 * Array and associative array operations. If a $(D
1133 * VariantN) contains an (associative) array, it can be indexed
1134 * into. Otherwise, an exception is thrown.
1136 inout(Variant
) opIndex(K
)(K i
) inout
1138 auto result
= Variant(i
);
1139 fptr(OpID
.index
, cast(ubyte[size
]*) &store
, &result
) == 0 ||
assert(false);
1147 Variant a
= new int[10];
1153 int[int] hash
= [ 42:24 ];
1155 assert(a
[42] == 24);
1157 assert(a
[42] == 12);
1161 Variant
opIndexAssign(T
, N
)(T value
, N i
)
1163 static if (AllowedTypes
.length
&& !isInstanceOf
!(.VariantN
, T
))
1165 enum canAssign(U
) = __traits(compiles
, (U u
){ u
[i
] = value
; });
1166 static assert(anySatisfy
!(canAssign
, AllowedTypes
),
1167 "Cannot assign " ~ T
.stringof
~ " to " ~ VariantN
.stringof
~
1168 " indexed with " ~ N
.stringof
);
1170 Variant
[2] args
= [ Variant(value
), Variant(i
) ];
1171 fptr(OpID
.indexAssign
, &store
, &args
) == 0 ||
assert(false);
1176 Variant
opIndexOpAssign(string op
, T
, N
)(T value
, N i
)
1178 return opIndexAssign(mixin(`opIndex(i)` ~ op
~ `value`), i
);
1181 /** If the `VariantN` contains an (associative) array,
1182 * returns the _length of that array. Otherwise, throws an
1185 @property size_t
length()
1187 return cast(size_t
) fptr(OpID
.length
, &store
, null);
1191 If the `VariantN` contains an array, applies `dg` to each
1192 element of the array in turn. Otherwise, throws an exception.
1194 int opApply(Delegate
)(scope Delegate dg
)
1195 if (is(Delegate
== delegate))
1197 alias A
= Parameters
!(Delegate
)[0];
1198 if (type
== typeid(A
[]))
1200 auto arr
= get
!(A
[]);
1201 foreach (ref e
; arr
)
1203 if (dg(e
)) return 1;
1206 else static if (is(A
== VariantN
))
1208 foreach (i
; 0 .. length
)
1210 // @@@TODO@@@: find a better way to not confuse
1211 // clients who think they change values stored in the
1212 // Variant when in fact they are only changing tmp.
1214 debug scope(exit
) assert(tmp
== this[i
]);
1215 if (dg(tmp
)) return 1;
1220 import std
.conv
: text
;
1221 import std
.exception
: enforce
;
1222 enforce(false, text("Variant type ", type
,
1223 " not iterable with values of type ",
1233 alias Var
= VariantN
!(maxSize
!(int, double, string
));
1235 Var a
; // Must assign before use, otherwise exception ensues
1236 // Initialize with an integer; make the type int
1238 assert(b
.type
== typeid(int));
1239 // Peek at the value
1240 assert(b
.peek
!(int) !is null && *b
.peek
!(int) == 42);
1241 // Automatically convert per language rules
1242 auto x
= b
.get
!(real);
1244 // Assign any other type, including other variants
1247 assert(a
.type
== typeid(double));
1248 // Implicit conversions work just as with built-in types
1250 // Check for convertibility
1251 assert(!a
.convertsTo
!(int)); // double not convertible to int
1252 // Strings and all other arrays are supported
1253 a
= "now I'm a string";
1254 assert(a
== "now I'm a string");
1257 /// can also assign arrays
1260 alias Var
= VariantN
!(maxSize
!(int[]));
1262 Var a
= new int[42];
1263 assert(a
.length
== 42);
1270 alias V
= VariantN
!24;
1271 const alignMask
= V
.alignof
- 1;
1272 assert(V
.sizeof
== ((24 + (void*).sizeof
+ alignMask
) & ~alignMask
));
1275 /// Can also assign class values
1278 alias Var
= VariantN
!(maxSize
!(int*)); // classes are pointers
1284 assert(*a
.peek
!(Foo
) == foo
); // and full type information is preserved
1289 import std
.conv
: to
;
1291 int foo() { return 42; }
1295 static int bar(string s
) { return to
!int(s
); }
1297 assert(v("43") == 43);
1302 int[int] hash
= [ 42:24 ];
1304 assert(v
[42] == 24);
1309 // opIndex with static arrays, https://issues.dlang.org/show_bug.cgi?id=12771
1312 int[4] elements
= [0, 1, 2, 3];
1313 Variant v
= elements
;
1314 assert(v
== elements
);
1319 assert(v
!= elements
);
1324 import std
.exception
: assertThrown
;
1325 Algebraic
!(int[]) v
= [2, 2];
1327 assert(v
== [2, 2]);
1330 assert(v
!= [2, 2]);
1332 // opIndexAssign from Variant
1336 static assert(!__traits(compiles
, (v
[1] = null)));
1337 assertThrown
!VariantException(v
[1] = Variant(null));
1340 // https://issues.dlang.org/show_bug.cgi?id=10879
1343 int[10] arr
= [1,2,3,4,5,6,7,8,9,10];
1354 static struct LargeStruct
1366 // https://issues.dlang.org/show_bug.cgi?id=8195
1378 static assert(S
.sizeof
>= Variant
.sizeof
);
1379 alias Types
= AliasSeq
!(string
, int, S
);
1380 alias MyVariant
= VariantN
!(maxSize
!Types
, Types
);
1382 auto v
= MyVariant(S
.init
);
1383 assert(v
== S
.init
);
1386 // https://issues.dlang.org/show_bug.cgi?id=10961
1389 // Primarily test that we can assign a void[] to a Variant.
1390 void[] elements
= cast(void[])[1, 2, 3];
1391 Variant v
= elements
;
1392 void[] returned
= v
.get
!(void[]);
1393 assert(returned
== elements
);
1396 // https://issues.dlang.org/show_bug.cgi?id=13352
1399 alias TP
= Algebraic
!(long);
1402 assert(!TP
.allowed
!ulong);
1403 assert(a
+ b
== 3L);
1404 assert(a
+ 2 == 3L);
1405 assert(1 + b
== 3L);
1407 alias TP2
= Algebraic
!(long, string
);
1409 assert(a
+ c
== 4L);
1412 // https://issues.dlang.org/show_bug.cgi?id=13354
1415 alias A
= Algebraic
!(string
[]);
1417 assert(a
[0] == "a");
1418 assert(a
[1] == "b");
1420 assert(a
[1] == "c");
1422 alias AA
= Algebraic
!(int[string
]);
1423 AA aa
= ["a": 1, "b": 2];
1424 assert(aa
["a"] == 1);
1425 assert(aa
["b"] == 2);
1427 assert(aa
["b"] == 3);
1430 // https://issues.dlang.org/show_bug.cgi?id=14198
1434 assert(a
.type
== typeid(bool));
1437 // https://issues.dlang.org/show_bug.cgi?id=14233
1440 alias Atom
= Algebraic
!(string
, This
[]);
1443 auto a
= Atom(values
);
1449 Algebraic
!(int, double) a
;
1454 // https://issues.dlang.org/show_bug.cgi?id=14457
1457 alias A
= Algebraic
!(int, float, double);
1458 alias B
= Algebraic
!(int, float);
1464 assert(a
.type
== typeid(float));
1465 assert(a
.get
!float == 6f);
1468 // https://issues.dlang.org/show_bug.cgi?id=14585
1474 ~this() {assert(x
== 42);}
1479 // https://issues.dlang.org/show_bug.cgi?id=14586
1482 const Variant v
= new immutable Object
;
1483 v
.get
!(immutable Object
);
1490 T
opCast(T
)() {assert(false);}
1496 // https://issues.dlang.org/show_bug.cgi?id=13262
1499 static void fun(T
)(Variant v
){
1505 fun
!(shared(int))(v
);
1506 fun
!(shared(int)[])(v
);
1528 fun
!(shared(S1
))(v
);
1530 fun
!(shared(S2
))(v
);
1532 fun
!(shared(S3
))(v
);
1534 // ensure structs that are shared, but don't have shared postblits
1543 static assert(!is(typeof(fun
!(shared(S4
))(v
))));
1555 Algebraic
!(SafeS
) y
;
1558 // https://issues.dlang.org/show_bug.cgi?id=19986
1562 v
= const(ubyte[33]).init
;
1573 // https://issues.dlang.org/show_bug.cgi?id=21021
1586 auto other
= a
.get
!S
;
1587 assert(msg
.array
[0] == 3);
1588 assert(other
.array
[0] == 3);
1591 // https://issues.dlang.org/show_bug.cgi?id=21231
1592 // Compatibility with -preview=fieldwise
1597 bool opCmp(const scope ref Empty
) const
1605 VariantN
!(4, Empty
) v
= a
;
1610 // Compatibility with -preview=fieldwise
1615 bool opEquals(const scope ref Empty
) const
1622 VariantN
!(4, Empty
) v
= a
;
1626 // https://issues.dlang.org/show_bug.cgi?id=22647
1627 // Can compare with 'null'
1640 Variant v
= Foo
.init
; // 'null'
1641 assert(v
!= null); // can only compare objects with 'null' by using 'is'
1644 assert(v
== null); // pointers can be compared with 'null'
1647 assert(v
== null); // arrays can be compared with 'null'
1650 assert(v
== null); // strings are arrays, an empty string is considered 'null'
1653 assert(v
== null); // works with alias this
1659 assert(!(v
< null));
1663 _Algebraic data type restricted to a closed set of possible
1664 types. It's an alias for $(LREF VariantN) with an
1665 appropriately-constructed maximum size. `Algebraic` is
1666 useful when it is desirable to restrict what a discriminated type
1667 could hold to the end of defining simpler and more efficient
1670 $(RED Warning: $(LREF Algebraic) is outdated and not recommended for use in new
1671 code. Instead, use $(REF SumType, std,sumtype).)
1673 template Algebraic(T
...)
1675 alias Algebraic
= VariantN
!(maxSize
!T
, T
);
1681 auto v
= Algebraic
!(int, double, string
)(5);
1682 assert(v
.peek
!(int));
1684 assert(v
.peek
!(double));
1685 // auto x = v.peek!(long); // won't compile, type long not allowed
1686 // v = '1'; // won't compile, type char not allowed
1690 $(H4 Self-Referential Types)
1692 A useful and popular use of algebraic data structures is for defining $(LUCKY
1693 self-referential data structures), i.e. structures that embed references to
1694 values of their own type within.
1696 This is achieved with `Algebraic` by using `This` as a placeholder whenever a
1697 reference to the type being defined is needed. The `Algebraic` instantiation
1698 will perform $(LINK2 https://en.wikipedia.org/wiki/Name_resolution_(programming_languages)#Alpha_renaming_to_make_name_resolution_trivial,
1699 alpha renaming) on its constituent types, replacing `This`
1700 with the self-referenced type. The structure of the type involving `This` may
1701 be arbitrarily complex.
1705 import std
.typecons
: Tuple
, tuple
;
1707 // A tree is either a leaf or a branch of two other trees
1708 alias Tree(Leaf
) = Algebraic
!(Leaf
, Tuple
!(This
*, This
*));
1709 Tree
!int tree
= tuple(new Tree
!int(42), new Tree
!int(43));
1710 Tree
!int* right
= tree
.get
!1[1];
1711 assert(*right
== 43);
1713 // An object is a double, a string, or a hash of objects
1714 alias Obj
= Algebraic
!(double, string
, This
[string
]);
1716 assert(obj
.get
!1 == "hello");
1718 assert(obj
.get
!0 == 42);
1719 obj
= ["customer": Obj("John"), "paid": Obj(23.95)];
1720 assert(obj
.get
!2["customer"] == "John");
1723 private struct FakeComplexReal
1729 Alias for $(LREF VariantN) instantiated with the largest size of `creal`,
1730 `char[]`, and `void delegate()`. This ensures that `Variant` is large enough
1731 to hold all of D's predefined types unboxed, including all numeric types,
1732 pointers, delegates, and class references. You may want to use
1733 `VariantN` directly with a different maximum size either for
1734 storing larger types unboxed, or for saving memory.
1736 alias Variant
= VariantN
!(maxSize
!(FakeComplexReal
, char[], void delegate()));
1741 Variant a
; // Must assign before use, otherwise exception ensues
1742 // Initialize with an integer; make the type int
1744 assert(b
.type
== typeid(int));
1745 // Peek at the value
1746 assert(b
.peek
!(int) !is null && *b
.peek
!(int) == 42);
1747 // Automatically convert per language rules
1748 auto x
= b
.get
!(real);
1750 // Assign any other type, including other variants
1753 assert(a
.type
== typeid(double));
1754 // Implicit conversions work just as with built-in types
1756 // Check for convertibility
1757 assert(!a
.convertsTo
!(int)); // double not convertible to int
1758 // Strings and all other arrays are supported
1759 a
= "now I'm a string";
1760 assert(a
== "now I'm a string");
1763 /// can also assign arrays
1766 Variant a
= new int[42];
1767 assert(a
.length
== 42);
1772 /// Can also assign class values
1780 assert(*a
.peek
!(Foo
) == foo
); // and full type information is preserved
1784 * Returns an array of variants constructed from `args`.
1786 * This is by design. During construction the `Variant` needs
1787 * static type information about the type being held, so as to store a
1788 * pointer to function for fast retrieval.
1790 Variant
[] variantArray(T
...)(T args
)
1795 result
~= Variant(arg
);
1803 auto a
= variantArray(1, 3.14, "Hi!");
1804 assert(a
[1] == 3.14);
1805 auto b
= Variant(a
); // variant array as variant
1806 assert(b
[1] == 3.14);
1810 * Thrown in three cases:
1812 * $(OL $(LI An uninitialized `Variant` is used in any way except
1813 * assignment and `hasValue`;) $(LI A `get` or
1814 * `coerce` is attempted with an incompatible target type;)
1815 * $(LI A comparison between `Variant` objects of
1816 * incompatible types is attempted.))
1820 // @@@ BUG IN COMPILER. THE 'STATIC' BELOW SHOULD NOT COMPILE
1821 static class VariantException
: Exception
1823 /// The source type in the conversion or comparison
1825 /// The target type in the conversion or comparison
1831 this(TypeInfo source
, TypeInfo target
)
1833 super("Variant: attempting to use incompatible types "
1835 ~ " and " ~ target
.toString());
1836 this.source
= source
;
1837 this.target
= target
;
1844 import std
.exception
: assertThrown
;
1848 // uninitialized use
1849 assertThrown
!VariantException(v
+ 1);
1850 assertThrown
!VariantException(v
.length
);
1852 // .get with an incompatible target type
1853 assertThrown
!VariantException(Variant("a").get
!int);
1855 // comparison between incompatible types
1856 assertThrown
!VariantException(Variant(3) < Variant("a"));
1861 alias W1
= This2Variant
!(char, int, This
[int]);
1862 alias W2
= AliasSeq
!(int, char[int]);
1863 static assert(is(W1
== W2
));
1865 alias var_t
= Algebraic
!(void, string
);
1871 alias A
= Algebraic
!(real, This
[], This
[int], This
[This
]);
1876 auto v
= v1
.peek
!(A
[]);
1886 import std
.conv
: ConvException
;
1887 import std
.exception
: assertThrown
, collectException
;
1888 // try it with an oddly small size
1890 assert(test.size
> 1);
1892 // variantArray tests
1893 auto heterogeneous
= variantArray(1, 4.5, "hi");
1894 assert(heterogeneous
.length
== 3);
1895 auto variantArrayAsVariant
= Variant(heterogeneous
);
1896 assert(variantArrayAsVariant
[0] == 1);
1897 assert(variantArrayAsVariant
.length
== 3);
1900 auto arr
= Variant([1.2].dup
);
1904 assert(arr
[0] == 2);
1906 assert(arr
[1] == 4.5);
1910 auto b
= Variant(5);
1911 assert(!b
.peek
!(real) && b
.peek
!(int));
1915 assert(a
== b
, a
.type
.toString() ~ " " ~ b
.type
.toString());
1916 auto c
= Variant("this is a string");
1918 // comparison via implicit conversions
1919 a
= 42; b
= 42.0; assert(a
== b
);
1921 // try failing conversions
1922 bool failed
= false;
1925 auto d
= c
.get
!(int);
1929 //writeln(stderr, e.toString);
1932 assert(failed
); // :o)
1935 a
= Variant(42); assert(a
.toString() == "42");
1936 a
= Variant(42.22); assert(a
.toString() == "42.22");
1939 a
= Variant(42.22); assert(a
.coerce
!(int) == 42);
1940 a
= cast(short) 5; assert(a
.coerce
!(double) == 5);
1941 a
= Variant("10"); assert(a
.coerce
!int == 10);
1944 assert(a
.coerce
!bool);
1946 assert(!a
.coerce
!bool);
1949 assert(a
.coerce
!bool);
1951 assert(!a
.coerce
!bool);
1952 a
= Variant(float.init
);
1953 assertThrown
!ConvException(a
.coerce
!bool);
1955 a
= Variant("true");
1956 assert(a
.coerce
!bool);
1957 a
= Variant("false");
1958 assert(!a
.coerce
!bool);
1960 assertThrown
!ConvException(a
.coerce
!bool);
1966 assert(a
.coerce
!(B1
) !is null);
1968 assert(collectException(a
.coerce
!(B2
) is null));
1969 a
= cast(Object
) new B2
; // lose static type info; should still work
1970 assert(a
.coerce
!(B2
) !is null);
1972 // struct Big { int a[45]; }
1976 assert(a
.toHash() != 0);
1979 // tests adapted from
1980 // http://www.dsource.org/projects/tango/browser/trunk/tango/core/Variant.d?rev=2601
1985 assert(!v
.hasValue
);
1987 assert( v
.peek
!(int) );
1988 assert( v
.convertsTo
!(long) );
1989 assert( v
.get
!(int) == 42 );
1990 assert( v
.get
!(long) == 42L );
1991 assert( v
.get
!(ulong) == 42uL );
1993 v
= "Hello, World!";
1994 assert( v
.peek
!(string
) );
1996 assert( v
.get
!(string
) == "Hello, World!" );
1997 assert(!is(char[] : wchar[]));
1998 assert( !v
.convertsTo
!(wchar[]) );
1999 assert( v
.get
!(string
) == "Hello, World!" );
2001 // Literal arrays are dynamically-typed
2002 v
= cast(int[4]) [1,2,3,4];
2003 assert( v
.peek
!(int[4]) );
2004 assert( v
.get
!(int[4]) == [1,2,3,4] );
2008 assert( v
.peek
!(int[]) );
2009 assert( v
.get
!(int[]) == [1,2,3,4,5] );
2013 assert( v
.peek
!(double) );
2014 assert( v
.convertsTo
!(real) );
2015 //@@@ BUG IN COMPILER: DOUBLE SHOULD NOT IMPLICITLY CONVERT TO FLOAT
2016 assert( v
.convertsTo
!(float) );
2017 assert( *v
.peek
!(double) == 3.1413 );
2019 auto u
= Variant(v
);
2020 assert( u
.peek
!(double) );
2021 assert( *u
.peek
!(double) == 3.1413 );
2025 assert( v
+ 4 == 42 );
2026 assert( 4 + v
== 42 );
2027 assert( v
- 4 == 34 );
2028 assert( Variant(4) - v
== -34 );
2029 assert( v
* 2 == 76 );
2030 assert( 2 * v
== 76 );
2031 assert( v
/ 2 == 19 );
2032 assert( Variant(2) / v
== 0 );
2033 assert( v
% 2 == 0 );
2034 assert( Variant(2) % v
== 2 );
2035 assert( (v
& 6) == 6 );
2036 assert( (6 & v
) == 6 );
2037 assert( (v |
9) == 47 );
2038 assert( (9 | v
) == 47 );
2039 assert( (v ^
5) == 35 );
2040 assert( (5 ^ v
) == 35 );
2041 assert( v
<< 1 == 76 );
2042 assert( Variant(1) << Variant(2) == 4 );
2043 assert( v
>> 1 == 19 );
2044 assert( Variant(4) >> Variant(2) == 1 );
2045 assert( Variant("abc") ~ "def" == "abcdef" );
2046 assert( Variant("abc") ~ Variant("def") == "abcdef" );
2051 v
= 38; v
-= 4; assert( v
== 34 );
2052 v
= 38; v
*= 2; assert( v
== 76 );
2053 v
= 38; v
/= 2; assert( v
== 19 );
2054 v
= 38; v
%= 2; assert( v
== 0 );
2055 v
= 38; v
&= 6; assert( v
== 6 );
2056 v
= 38; v |
= 9; assert( v
== 47 );
2057 v
= 38; v ^
= 5; assert( v
== 35 );
2058 v
= 38; v
<<= 1; assert( v
== 76 );
2059 v
= 38; v
>>= 1; assert( v
== 19 );
2060 v
= 38; v
+= 1; assert( v
< 40 );
2064 assert( v
== "abcdef", *v
.peek
!(char[]) );
2065 assert( Variant(0) < Variant(42) );
2066 assert( Variant(42) > Variant(0) );
2067 assert( Variant(42) > Variant(0.1) );
2068 assert( Variant(42.1) > Variant(1) );
2069 assert( Variant(21) == Variant(21) );
2070 assert( Variant(0) != Variant(42) );
2071 assert( Variant("bar") == Variant("bar") );
2072 assert( Variant("foo") != Variant("bar") );
2075 auto v1
= Variant(42);
2076 auto v2
= Variant("foo");
2082 assert( hash
[v1
] == 0 );
2083 assert( hash
[v2
] == 1 );
2091 Variant vhash
= hash
;
2093 assert( vhash
.get
!(int[char[]])["a"] == 1 );
2094 assert( vhash
.get
!(int[char[]])["b"] == 2 );
2095 assert( vhash
.get
!(int[char[]])["c"] == 3 );
2101 // check comparisons incompatible with AllowedTypes
2102 Algebraic
!int v
= 2;
2106 static assert(!__traits(compiles
, () => v
== long.max
));
2107 static assert(!__traits(compiles
, () => v
== null));
2108 static assert(!__traits(compiles
, () => v
< long.max
));
2109 static assert(!__traits(compiles
, () => v
> null));
2112 // https://issues.dlang.org/show_bug.cgi?id=1558
2117 assert((va
+vb
).get
!(int) == -1);
2118 assert((va
-vb
).get
!(int) == 3);
2128 c
= variantArray(1, 2, 3.0, "hello", 4);
2129 assert(c
[3] == "hello");
2135 assert(!__traits(compiles
, v
.coerce
!(bool delegate())));
2142 real a
, b
, c
, d
, e
, f
, g
;
2148 v
= huge
; // Compile time error.
2149 assert(v
.get
!(Huge
).e
== 42);
2154 const x
= Variant(42);
2155 auto y1
= x
.get
!(const int);
2157 //auto y2 = x.get!(immutable int)();
2163 auto v
= Variant([ 1, 2, 3, 4 ][]);
2172 // test convertibility
2175 auto v
= Variant("abc".dup
);
2176 assert(v
.convertsTo
!(char[]));
2179 // https://issues.dlang.org/show_bug.cgi?id=5424
2185 static class AC
: A
{
2192 Variant b
= Variant(a
);
2195 // https://issues.dlang.org/show_bug.cgi?id=7070
2202 // Class and interface opEquals, https://issues.dlang.org/show_bug.cgi?id=12157
2207 class DerivedFoo
: Foo
{ }
2210 Foo f2
= new DerivedFoo();
2212 Variant v1
= f1
, v2
= f2
;
2214 assert(v1
!= new Foo());
2220 // Const parameters with opCall, https://issues.dlang.org/show_bug.cgi?id=11361
2223 static string
t1(string c
) {
2227 static const(char)[] t2(const(char)[] p
) {
2231 static char[] t3(int p
) {
2232 import std
.conv
: text
;
2240 assert(v1("abc") == "abca");
2241 assert(v1("abc").type
== typeid(string
));
2242 assert(v2("abc") == "abcb");
2244 assert(v2(cast(char[])("abc".dup
)) == "abcb");
2245 assert(v2("abc").type
== typeid(const(char)[]));
2247 assert(v3(4) == ['4']);
2248 assert(v3(4).type
== typeid(char[]));
2251 // https://issues.dlang.org/show_bug.cgi?id=12071
2254 static struct Structure
{ int data
; }
2255 alias VariantTest
= Algebraic
!(Structure
delegate() pure nothrow @nogc @safe);
2257 bool called
= false;
2258 Structure
example() pure nothrow @nogc @safe
2261 return Structure
.init
;
2263 auto m
= VariantTest(&example
);
2268 // Ordering comparisons of incompatible types
2269 // e.g. https://issues.dlang.org/show_bug.cgi?id=7990
2272 import std
.exception
: assertThrown
;
2273 assertThrown
!VariantException(Variant(3) < "a");
2274 assertThrown
!VariantException("a" < Variant(3));
2275 assertThrown
!VariantException(Variant(3) < Variant("a"));
2277 assertThrown
!VariantException(Variant
.init
< Variant(3));
2278 assertThrown
!VariantException(Variant(3) < Variant
.init
);
2281 // Handling of unordered types
2282 // https://issues.dlang.org/show_bug.cgi?id=9043
2285 import std
.exception
: assertThrown
;
2286 static struct A
{ int a
; }
2288 assert(Variant(A(3)) != A(4));
2290 assertThrown
!VariantException(Variant(A(3)) < A(4));
2291 assertThrown
!VariantException(A(3) < Variant(A(4)));
2292 assertThrown
!VariantException(Variant(A(3)) < Variant(A(4)));
2295 // Handling of empty types and arrays
2296 // https://issues.dlang.org/show_bug.cgi?id=10958
2299 class EmptyClass
{ }
2300 struct EmptyStruct
{ }
2301 alias EmptyArray
= void[0];
2302 alias Alg
= Algebraic
!(EmptyClass
, EmptyStruct
, EmptyArray
);
2304 Variant
testEmpty(T
)()
2308 assert(v
.get
!T
== inst
);
2309 assert(v
.peek
!T
!is null);
2310 assert(*v
.peek
!T
== inst
);
2312 assert(alg
.get
!T
== inst
);
2316 testEmpty
!EmptyClass();
2317 testEmpty
!EmptyStruct();
2318 testEmpty
!EmptyArray();
2320 // EmptyClass/EmptyStruct sizeof is 1, so we have this to test just size 0.
2321 EmptyArray arr
= EmptyArray
.init
;
2322 Algebraic
!(EmptyArray
) a
= arr
;
2323 assert(a
.length
== 0);
2324 assert(a
.get
!EmptyArray
== arr
);
2327 // Handling of void function pointers / delegates
2328 // https://issues.dlang.org/show_bug.cgi?id=11360
2331 static void t1() { }
2333 assert(v() == Variant
.init
);
2335 static int t2() { return 3; }
2340 // Using peek for large structs
2341 // https://issues.dlang.org/show_bug.cgi?id=8580
2344 struct TestStruct(bool pad
)
2348 ubyte[Variant
.size
] padding
;
2352 void testPeekWith(T
)()
2358 T
* original
= v
.peek
!T
;
2359 assert(original
.val1
== 3);
2360 assert(original
.val2
== 4);
2363 T modified
= v
.get
!T
;
2364 assert(modified
.val1
== 6);
2365 assert(modified
.val2
== 8);
2368 testPeekWith
!(TestStruct
!false)();
2369 testPeekWith
!(TestStruct
!true)();
2372 // https://issues.dlang.org/show_bug.cgi?id=18780
2377 assert(a
.convertsTo
!ulong);
2378 assert(a
.convertsTo
!uint);
2382 * Applies a delegate or function to the given $(LREF Algebraic) depending on the held type,
2383 * ensuring that all types are handled by the visiting functions.
2385 * The delegate or function having the currently held value as parameter is called
2386 * with `variant`'s current value. Visiting handlers are passed
2387 * in the template parameter list.
2388 * It is statically ensured that all held types of
2389 * `variant` are handled across all handlers.
2390 * `visit` allows delegates and static functions to be passed
2393 * If a function with an untyped parameter is specified, this function is called
2394 * when the variant contains a type that does not match any other function.
2395 * This can be used to apply the same function across multiple possible types.
2396 * Exactly one generic function is allowed.
2398 * If a function without parameters is specified, this function is called
2399 * when `variant` doesn't hold a value. Exactly one parameter-less function
2402 * Duplicate overloads matching the same type in one of the visitors are disallowed.
2404 * Returns: The return type of visit is deduced from the visiting functions and must be
2405 * the same across all overloads.
2406 * Throws: $(LREF VariantException) if `variant` doesn't hold a value and no
2407 * parameter-less fallback function is specified.
2409 template visit(Handlers
...)
2410 if (Handlers
.length
> 0)
2413 auto visit(VariantType
)(VariantType variant
)
2414 if (isAlgebraic
!VariantType
)
2416 return visitImpl
!(true, VariantType
, Handlers
)(variant
);
2423 Algebraic
!(int, string
) variant
;
2426 assert(variant
.visit
!((string s
) => cast(int) s
.length
,
2430 assert(variant
.visit
!((int i
) => i
,
2431 (string s
) => cast(int) s
.length
)()
2434 // Error function usage
2435 Algebraic
!(int, string
) emptyVar
;
2436 auto rslt
= emptyVar
.visit
!((string s
) => cast(int) s
.length
,
2441 // Generic function usage
2442 Algebraic
!(int, float, real) number
= 2;
2443 assert(number
.visit
!(x
=> x
+= 1) == 3);
2445 // Generic function for int/float with separate behavior for string
2446 Algebraic
!(int, float, string
) something
= 2;
2447 assert(something
.visit
!((string s
) => s
.length
, x
=> x
) == 2); // generic
2449 assert(something
.visit
!((string s
) => s
.length
, x
=> x
) == 4); // string
2451 // Generic handler and empty handler
2452 Algebraic
!(int, float, real) empty2
;
2453 assert(empty2
.visit
!(x
=> x
+ 1, () => -1) == -1);
2458 Algebraic
!(size_t
, string
) variant
;
2460 // not all handled check
2461 static assert(!__traits(compiles
, variant
.visit
!((size_t i
){ })() ));
2463 variant
= cast(size_t
) 10;
2465 variant
.visit
!( (string s
) => which
= 1,
2466 (size_t i
) => which
= 0
2469 // integer overload was called
2472 // mustn't compile as generic Variant not supported
2474 static assert(!__traits(compiles
, v
.visit
!((string s
) => which
= 1,
2475 (size_t i
) => which
= 0
2479 static size_t
func(string s
) {
2484 assert( 4 == variant
.visit
!(func
,
2488 Algebraic
!(int, float, string
) variant2
= 5.0f;
2489 // Shouldn' t compile as float not handled by visitor.
2490 static assert(!__traits(compiles
, variant2
.visit
!(
2494 Algebraic
!(size_t
, string
, float) variant3
;
2496 auto floatVisited
= false;
2498 assert(variant3
.visit
!(
2499 (float f
) { floatVisited
= true; return cast(size_t
) f
; },
2501 (size_t i
) { return i
; }
2503 assert(floatVisited
== true);
2505 Algebraic
!(float, string
) variant4
;
2507 assert(variant4
.visit
!(func
, (float f
) => cast(size_t
) f
, () => size_t
.max
)() == size_t
.max
);
2509 // double error func check
2510 static assert(!__traits(compiles
,
2511 visit
!(() => size_t
.max
, func
, (float f
) => cast(size_t
) f
, () => size_t
.max
)(variant4
))
2515 // disallow providing multiple generic handlers to visit
2516 // disallow a generic handler that does not apply to all types
2519 Algebraic
!(int, float) number
= 2;
2520 // ok, x + 1 valid for int and float
2521 static assert( __traits(compiles
, number
.visit
!(x
=> x
+ 1)));
2522 // bad, two generic handlers
2523 static assert(!__traits(compiles
, number
.visit
!(x
=> x
+ 1, x
=> x
+ 2)));
2524 // bad, x ~ "a" does not apply to int or float
2525 static assert(!__traits(compiles
, number
.visit
!(x
=> x
~ "a")));
2526 // bad, x ~ "a" does not apply to int or float
2527 static assert(!__traits(compiles
, number
.visit
!(x
=> x
+ 1, x
=> x
~ "a")));
2529 Algebraic
!(int, string
) maybenumber
= 2;
2530 // ok, x ~ "a" valid for string, x + 1 valid for int, only 1 generic
2531 static assert( __traits(compiles
, maybenumber
.visit
!((string x
) => x
~ "a", x
=> "foobar"[0 .. x
+ 1])));
2532 // bad, x ~ "a" valid for string but not int
2533 static assert(!__traits(compiles
, maybenumber
.visit
!(x
=> x
~ "a")));
2534 // bad, two generics, each only applies in one case
2535 static assert(!__traits(compiles
, maybenumber
.visit
!(x
=> x
+ 1, x
=> x
~ "a")));
2539 * Behaves as $(LREF visit) but doesn't enforce that all types are handled
2540 * by the visiting functions.
2542 * If a parameter-less function is specified it is called when
2543 * either `variant` doesn't hold a value or holds a type
2544 * which isn't handled by the visiting functions.
2546 * Returns: The return type of tryVisit is deduced from the visiting functions and must be
2547 * the same across all overloads.
2548 * Throws: $(LREF VariantException) if `variant` doesn't hold a value or
2549 * `variant` holds a value which isn't handled by the visiting functions,
2550 * when no parameter-less fallback function is specified.
2552 template tryVisit(Handlers
...)
2553 if (Handlers
.length
> 0)
2556 auto tryVisit(VariantType
)(VariantType variant
)
2557 if (isAlgebraic
!VariantType
)
2559 return visitImpl
!(false, VariantType
, Handlers
)(variant
);
2566 Algebraic
!(int, string
) variant
;
2570 variant
.tryVisit
!((int i
) { which
= 0; })();
2573 // Error function usage
2575 variant
.tryVisit
!((int i
) { which
= 0; },
2576 () { which
= -100; })();
2577 assert(which
== -100);
2582 import std
.exception
: assertThrown
;
2583 Algebraic
!(int, string
) variant
;
2587 variant
.tryVisit
!((int i
){ which
= 0; })();
2593 assertThrown
!VariantException(variant
.tryVisit
!((int i
) { which
= 0; })());
2600 variant
.tryVisit
!((int i
) { which
= 0; }, errorfunc
)();
2602 assert(which
== -1);
2605 private template isAlgebraic(Type
)
2607 static if (is(Type _
== VariantN
!T
, T
...))
2608 enum isAlgebraic
= T
.length
>= 2; // T[0] == maxDataSize, T[1..$] == AllowedTypesParam
2610 enum isAlgebraic
= false;
2615 static assert(!isAlgebraic
!(Variant
));
2616 static assert( isAlgebraic
!(Algebraic
!(string
)));
2617 static assert( isAlgebraic
!(Algebraic
!(int, int[])));
2620 private auto visitImpl(bool Strict
, VariantType
, Handler
...)(VariantType variant
)
2621 if (isAlgebraic
!VariantType
&& Handler
.length
> 0)
2623 alias AllowedTypes
= VariantType
.AllowedTypes
;
2627 * Returns: Struct where `indices` is an array which
2628 * contains at the n-th position the index in Handler which takes the
2629 * n-th type of AllowedTypes. If an Handler doesn't match an
2630 * AllowedType, -1 is set. If a function in the delegates doesn't
2631 * have parameters, the field `exceptionFuncIdx` is set;
2632 * otherwise it's -1.
2634 auto visitGetOverloadMap()
2637 int[AllowedTypes
.length
] indices
;
2638 int exceptionFuncIdx
= -1;
2639 int generalFuncIdx
= -1;
2644 enum int nonmatch
= ()
2646 foreach (int dgidx
, dg
; Handler
)
2649 foreach (T
; AllowedTypes
)
2651 found |
= __traits(compiles
, { static assert(isSomeFunction
!(dg
!T
)); });
2652 found |
= __traits(compiles
, (T t
) { dg(t
); });
2653 found |
= __traits(compiles
, dg());
2655 if (!found
) return dgidx
;
2659 static assert(nonmatch
== -1, "No match for visit handler #"~
2660 nonmatch
.stringof
~" ("~Handler
[nonmatch
].stringof
~")");
2662 foreach (tidx
, T
; AllowedTypes
)
2665 foreach (dgidx
, dg
; Handler
)
2667 // Handle normal function objects
2668 static if (isSomeFunction
!dg
)
2670 alias Params
= Parameters
!dg
;
2671 static if (Params
.length
== 0)
2673 // Just check exception functions in the first
2674 // inner iteration (over delegates)
2679 if (result
.exceptionFuncIdx
!= -1)
2680 assert(false, "duplicate parameter-less (error-)function specified");
2681 result
.exceptionFuncIdx
= dgidx
;
2684 else static if (is(Params
[0] == T
) ||
is(Unqual
!(Params
[0]) == T
))
2687 assert(false, "duplicate overload specified for type '" ~ T
.stringof
~ "'");
2690 result
.indices
[tidx
] = dgidx
;
2693 else static if (__traits(compiles
, { static assert(isSomeFunction
!(dg
!T
)); }))
2695 assert(result
.generalFuncIdx
== -1 ||
2696 result
.generalFuncIdx
== dgidx
,
2697 "Only one generic visitor function is allowed");
2698 result
.generalFuncIdx
= dgidx
;
2700 // Handle composite visitors with opCall overloads
2704 result
.indices
[tidx
] = -1;
2710 enum HandlerOverloadMap
= visitGetOverloadMap();
2712 if (!variant
.hasValue
)
2714 // Call the exception function. The HandlerOverloadMap
2715 // will have its exceptionFuncIdx field set to value != -1 if an
2716 // exception function has been specified; otherwise we just through an exception.
2717 static if (HandlerOverloadMap
.exceptionFuncIdx
!= -1)
2718 return Handler
[ HandlerOverloadMap
.exceptionFuncIdx
]();
2720 throw new VariantException("variant must hold a value before being visited.");
2723 foreach (idx
, T
; AllowedTypes
)
2725 if (auto ptr
= variant
.peek
!T
)
2727 enum dgIdx
= HandlerOverloadMap
.indices
[idx
];
2729 static if (dgIdx
== -1)
2731 static if (HandlerOverloadMap
.generalFuncIdx
>= 0)
2732 return Handler
[HandlerOverloadMap
.generalFuncIdx
](*ptr
);
2733 else static if (Strict
)
2734 static assert(false, "overload for type '" ~ T
.stringof
~ "' hasn't been specified");
2735 else static if (HandlerOverloadMap
.exceptionFuncIdx
!= -1)
2736 return Handler
[HandlerOverloadMap
.exceptionFuncIdx
]();
2738 throw new VariantException(
2739 "variant holds value of type '"
2741 "' but no visitor has been provided"
2746 return Handler
[ dgIdx
](*ptr
);
2754 // https://issues.dlang.org/show_bug.cgi?id=21253
2757 static struct A
{ int n
; }
2760 auto a
= Algebraic
!(A
, B
)(B());
2769 // validate that visit can be called with a const type
2770 struct Foo
{ int depth
; }
2771 struct Bar
{ int depth
; }
2772 alias FooBar
= Algebraic
!(Foo
, Bar
);
2774 int depth(in FooBar fb
) {
2775 return fb
.visit
!((Foo foo
) => foo
.depth
,
2776 (Bar bar
) => bar
.depth
);
2780 assert(depth(fb
) == 3);
2783 // https://issues.dlang.org/show_bug.cgi?id=16383
2786 class Foo
{this() immutable {}}
2787 alias V
= Algebraic
!(immutable Foo
);
2789 auto x
= V(new immutable Foo
).visit
!(
2790 (immutable(Foo
) _
) => 3
2795 // https://issues.dlang.org/show_bug.cgi?id=5310
2807 const Variant a
= [2];
2811 // https://issues.dlang.org/show_bug.cgi?id=10017
2816 ubyte[Variant
.size
+ 1] s
;
2820 v1
= S(); // the payload is allocated on the heap
2821 v2
= v1
; // AssertError: target must be non-null
2825 // https://issues.dlang.org/show_bug.cgi?id=7069
2828 import std
.exception
: assertThrown
;
2833 static foreach (qual
; AliasSeq
!(Alias
, ConstOf
))
2835 assert(v
.get
!(qual
!int) == 10);
2836 assert(v
.get
!(qual
!float) == 10.0f);
2838 static foreach (qual
; AliasSeq
!(ImmutableOf
, SharedOf
, SharedConstOf
))
2840 assertThrown
!VariantException(v
.get
!(qual
!int));
2845 static foreach (qual
; AliasSeq
!(ConstOf
))
2847 assert(v
.get
!(qual
!int) == 20);
2848 assert(v
.get
!(qual
!float) == 20.0f);
2850 static foreach (qual
; AliasSeq
!(Alias
, ImmutableOf
, SharedOf
, SharedConstOf
))
2852 assertThrown
!VariantException(v
.get
!(qual
!int));
2853 assertThrown
!VariantException(v
.get
!(qual
!float));
2856 immutable(int) ii
= ci
;
2858 static foreach (qual
; AliasSeq
!(ImmutableOf
, ConstOf
, SharedConstOf
))
2860 assert(v
.get
!(qual
!int) == 20);
2861 assert(v
.get
!(qual
!float) == 20.0f);
2863 static foreach (qual
; AliasSeq
!(Alias
, SharedOf
))
2865 assertThrown
!VariantException(v
.get
!(qual
!int));
2866 assertThrown
!VariantException(v
.get
!(qual
!float));
2871 static foreach (qual
; AliasSeq
!(Alias
, ConstOf
))
2873 assert(v
.get
!(qual
!(int[])) == [1,2,3]);
2874 assert(v
.get
!(qual
!(int)[]) == [1,2,3]);
2876 static foreach (qual
; AliasSeq
!(ImmutableOf
, SharedOf
, SharedConstOf
))
2878 assertThrown
!VariantException(v
.get
!(qual
!(int[])));
2879 assertThrown
!VariantException(v
.get
!(qual
!(int)[]));
2882 const(int[]) cai
= [4,5,6];
2884 static foreach (qual
; AliasSeq
!(ConstOf
))
2886 assert(v
.get
!(qual
!(int[])) == [4,5,6]);
2887 assert(v
.get
!(qual
!(int)[]) == [4,5,6]);
2889 static foreach (qual
; AliasSeq
!(Alias
, ImmutableOf
, SharedOf
, SharedConstOf
))
2891 assertThrown
!VariantException(v
.get
!(qual
!(int[])));
2892 assertThrown
!VariantException(v
.get
!(qual
!(int)[]));
2895 immutable(int[]) iai
= [7,8,9];
2897 //assert(v.get!(immutable(int[])) == [7,8,9]); // Bug ??? runtime error
2898 assert(v
.get
!(immutable(int)[]) == [7,8,9]);
2899 assert(v
.get
!(const(int[])) == [7,8,9]);
2900 assert(v
.get
!(const(int)[]) == [7,8,9]);
2901 //assert(v.get!(shared(const(int[]))) == cast(shared const)[7,8,9]); // Bug ??? runtime error
2902 //assert(v.get!(shared(const(int))[]) == cast(shared const)[7,8,9]); // Bug ??? runtime error
2903 static foreach (qual
; AliasSeq
!(Alias
))
2905 assertThrown
!VariantException(v
.get
!(qual
!(int[])));
2906 assertThrown
!VariantException(v
.get
!(qual
!(int)[]));
2913 static foreach (qual
; AliasSeq
!(Alias
, ConstOf
))
2915 assert(v
.get
!(qual
!B
) is b
);
2916 assert(v
.get
!(qual
!A
) is b
);
2917 assert(v
.get
!(qual
!Object
) is b
);
2919 static foreach (qual
; AliasSeq
!(ImmutableOf
, SharedOf
, SharedConstOf
))
2921 assertThrown
!VariantException(v
.get
!(qual
!B
));
2922 assertThrown
!VariantException(v
.get
!(qual
!A
));
2923 assertThrown
!VariantException(v
.get
!(qual
!Object
));
2926 const(B
) cb
= new B();
2928 static foreach (qual
; AliasSeq
!(ConstOf
))
2930 assert(v
.get
!(qual
!B
) is cb
);
2931 assert(v
.get
!(qual
!A
) is cb
);
2932 assert(v
.get
!(qual
!Object
) is cb
);
2934 static foreach (qual
; AliasSeq
!(Alias
, ImmutableOf
, SharedOf
, SharedConstOf
))
2936 assertThrown
!VariantException(v
.get
!(qual
!B
));
2937 assertThrown
!VariantException(v
.get
!(qual
!A
));
2938 assertThrown
!VariantException(v
.get
!(qual
!Object
));
2941 immutable(B
) ib
= new immutable(B
)();
2943 static foreach (qual
; AliasSeq
!(ImmutableOf
, ConstOf
, SharedConstOf
))
2945 assert(v
.get
!(qual
!B
) is ib
);
2946 assert(v
.get
!(qual
!A
) is ib
);
2947 assert(v
.get
!(qual
!Object
) is ib
);
2949 static foreach (qual
; AliasSeq
!(Alias
, SharedOf
))
2951 assertThrown
!VariantException(v
.get
!(qual
!B
));
2952 assertThrown
!VariantException(v
.get
!(qual
!A
));
2953 assertThrown
!VariantException(v
.get
!(qual
!Object
));
2956 shared(B
) sb
= new shared B();
2958 static foreach (qual
; AliasSeq
!(SharedOf
, SharedConstOf
))
2960 assert(v
.get
!(qual
!B
) is sb
);
2961 assert(v
.get
!(qual
!A
) is sb
);
2962 assert(v
.get
!(qual
!Object
) is sb
);
2964 static foreach (qual
; AliasSeq
!(Alias
, ImmutableOf
, ConstOf
))
2966 assertThrown
!VariantException(v
.get
!(qual
!B
));
2967 assertThrown
!VariantException(v
.get
!(qual
!A
));
2968 assertThrown
!VariantException(v
.get
!(qual
!Object
));
2971 shared(const(B
)) scb
= new shared const B();
2973 static foreach (qual
; AliasSeq
!(SharedConstOf
))
2975 assert(v
.get
!(qual
!B
) is scb
);
2976 assert(v
.get
!(qual
!A
) is scb
);
2977 assert(v
.get
!(qual
!Object
) is scb
);
2979 static foreach (qual
; AliasSeq
!(Alias
, ConstOf
, ImmutableOf
, SharedOf
))
2981 assertThrown
!VariantException(v
.get
!(qual
!B
));
2982 assertThrown
!VariantException(v
.get
!(qual
!A
));
2983 assertThrown
!VariantException(v
.get
!(qual
!Object
));
2987 // https://issues.dlang.org/show_bug.cgi?id=12540
2990 static struct DummyScope
2992 alias Alias12540
= Algebraic
!Class12540
;
2994 static class Class12540
3003 // https://issues.dlang.org/show_bug.cgi?id=10194
3004 // Also test for elaborate copying
3018 @disable S
opAssign();
3036 // assigning a new value should destroy the existing one
3040 // destroying the variant should destroy it's current value
3049 // https://issues.dlang.org/show_bug.cgi?id=13300
3056 static assert( hasElaborateCopyConstructor
!(Variant
));
3057 static assert(!hasElaborateCopyConstructor
!(Algebraic
!bool));
3058 static assert( hasElaborateCopyConstructor
!(Algebraic
!S
));
3059 static assert( hasElaborateCopyConstructor
!(Algebraic
!(bool, S
)));
3061 static assert( hasElaborateDestructor
!(Variant
));
3062 static assert(!hasElaborateDestructor
!(Algebraic
!bool));
3063 static assert( hasElaborateDestructor
!(Algebraic
!S
));
3064 static assert( hasElaborateDestructor
!(Algebraic
!(bool, S
)));
3067 alias Value
= Algebraic
!bool;
3074 auto a
= appender
!(T
[]);
3077 // https://issues.dlang.org/show_bug.cgi?id=13871
3080 alias A
= Algebraic
!(int, typeof(null));
3081 static struct B
{ A value
; }
3082 alias C
= std
.variant
.Algebraic
!B
;
3090 import std
.exception
: assertThrown
, assertNotThrown
;
3091 // Make sure Variant can handle types with opDispatch but no length field.
3092 struct SWithNoLength
3094 void opDispatch(string s
)() { }
3099 @property int opDispatch(string s
)()
3101 // Assume that s == "length"
3102 return 5; // Any value is OK for test.
3106 SWithNoLength sWithNoLength
;
3107 Variant v
= sWithNoLength
;
3108 assertThrown
!VariantException(v
.length
);
3110 SWithLength sWithLength
;
3112 assertNotThrown
!VariantException(v
.get
!SWithLength
.length
);
3113 assertThrown
!VariantException(v
.length
);
3116 // https://issues.dlang.org/show_bug.cgi?id=13534
3119 static assert(!__traits(compiles
, () @safe {
3120 auto foo() @system { return 3; }
3121 auto v
= Variant(&foo
);
3122 v(); // foo is called in safe code!?
3126 // https://issues.dlang.org/show_bug.cgi?id=15039
3129 import std
.typecons
;
3132 alias IntTypedef
= Typedef
!int;
3133 alias Obj
= Algebraic
!(int, IntTypedef
, This
[]);
3144 // https://issues.dlang.org/show_bug.cgi?id=15791
3148 struct NS1
{ int foo() { return n
+ 10; } }
3149 struct NS2
{ int foo() { return n
* 10; } }
3153 assert(v
.get
!NS1
.foo() == 13);
3155 assert(v
.get
!NS2
.foo() == 30);
3158 // https://issues.dlang.org/show_bug.cgi?id=15827
3161 static struct Foo15827
{ Variant v
; this(Foo15827 v
) {} }
3162 Variant v
= Foo15827
.init
;
3165 // https://issues.dlang.org/show_bug.cgi?id=18934
3177 Variant v2
= v
; // support copying from one variant to the other
3180 assert(v
.get
!S
.x
== 2);
3183 // https://issues.dlang.org/show_bug.cgi?id=19200
3188 static int opBinaryRight(string op
: "|", T
)(T rhs
)
3200 // https://issues.dlang.org/show_bug.cgi?id=11061
3203 int[4] el
= [0, 1, 2, 3];
3204 int[3] nl
= [0, 1, 2];
3206 assert(v1
== el
); // Compare Var(static) to static
3207 assert(v1
!= nl
); // Compare static arrays of different length
3208 assert(v1
== [0, 1, 2, 3]); // Compare Var(static) to dynamic.
3209 assert(v1
!= [0, 1, 2]);
3210 int[] dyn
= [0, 1, 2, 3];
3212 assert(v1
== el
); // Compare Var(dynamic) to static.
3213 assert(v1
== [0, 1] ~ [2, 3]); // Compare Var(dynamic) to dynamic
3216 // https://issues.dlang.org/show_bug.cgi?id=15940
3226 auto v
= Variant(s
); // compile error
3231 // Test if we don't have scoping issues.
3232 Variant
createVariant(int[] input
)
3234 int[2] el
= [input
[0], input
[1]];
3238 Variant v
= createVariant([0, 1]);
3239 createVariant([2, 3]);
3243 // https://issues.dlang.org/show_bug.cgi?id=19994
3246 alias Inner
= Algebraic
!(This
*);
3247 alias Outer
= Algebraic
!(Inner
, This
*);
3249 static assert(is(Outer
.AllowedTypes
== AliasSeq
!(Inner
, Outer
*)));
3252 // https://issues.dlang.org/show_bug.cgi?id=21296
3255 immutable aa
= ["0": 0];
3256 auto v
= Variant(aa
); // compile error