1 // Written in the D programming language.
2
3 /**
4 This module implements a
5 $(HTTP erdani.org/publications/cuj-04-2002.php.html,discriminated union)
6 type (a.k.a.
7 $(HTTP en.wikipedia.org/wiki/Tagged_union,tagged union),
8 $(HTTP en.wikipedia.org/wiki/Algebraic_data_type,algebraic type)).
9 Such types are useful
10 for type-uniform binary interfaces, interfacing with scripting
11 languages, and comfortable exploratory programming.
12
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
19 the current value.
20
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`).
25
26 $(RED Warning: $(LREF Algebraic) is outdated and not recommended for use in new
27 code. Instead, use $(REF SumType, std,sumtype).)
28
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)
36 */
37 module std.variant;
38
39 import std.meta, std.traits, std.typecons;
40
41 ///
42 @system unittest
43 {
44 Variant a; // Must assign before use, otherwise exception ensues
45 // Initialize with an integer; make the type int
46 Variant b = 42;
47 assert(b.type == typeid(int));
48 // Peek at the value
49 assert(b.peek!(int) !is null && *b.peek!(int) == 42);
50 // Automatically convert per language rules
51 auto x = b.get!(real);
52
53 // Assign any other type, including other variants
54 a = b;
55 a = 3.14;
56 assert(a.type == typeid(double));
57 // Implicit conversions work just as with built-in types
58 assert(a < b);
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");
64
65 // can also assign arrays
66 a = new int[42];
67 assert(a.length == 42);
68 a[5] = 7;
69 assert(a[5] == 7);
70
71 // Can also assign class values
72 class Foo {}
73 auto foo = new Foo;
74 a = foo;
75 assert(*a.peek!(Foo) == foo); // and full type information is preserved
76 }
77
78 /++
79 Gives the `sizeof` the largest type given.
80
81 See_Also: $(LINK https://forum.dlang.org/thread/wbpnncxepehgcswhuazl@forum.dlang.org?page=1)
82 +/
maxSize(Ts...)83 template maxSize(Ts...)
84 {
85 align(1) union Impl
86 {
87 static foreach (i, T; Ts)
88 {
89 static if (!is(T == void))
90 mixin("T _field_", i, ";");
91 }
92 }
93 enum maxSize = Impl.sizeof;
94 }
95
96 ///
97 @safe unittest
98 {
99 struct Cat { int a, b, c; }
100
101 align(1) struct S
102 {
103 long l;
104 ubyte b;
105 }
106
107 align(1) struct T
108 {
109 ubyte b;
110 long l;
111 }
112
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);
127 }
128
129 struct This;
130
131 private alias This2Variant(V, T...) = AliasSeq!(ReplaceTypeUnless!(isAlgebraic, This, V, T));
132
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...)
136 if (isTypeTuple!U)
137 {
138 static if (U.length == 0)
139 {
140 import std.algorithm.comparison : max;
141 enum maxVariantAlignment = max(real.alignof, size_t.alignof);
142 }
143 else
144 enum maxVariantAlignment = maxAlignment!(U);
145 }
146
147 /**
148 * Back-end type seldom used directly by user
149 * code. Two commonly-used types using `VariantN` are:
150 *
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.))
161 *
162 * Both `Algebraic` and `Variant` share $(D
163 * VariantN)'s interface. (See their respective documentations below.)
164 *
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).
172 *
173 */
VariantN(size_t maxDataSize,AllowedTypesParam...)174 struct VariantN(size_t maxDataSize, AllowedTypesParam...)
175 {
176 /**
177 The list of allowed types. If empty, any type is allowed.
178 */
179 alias AllowedTypes = This2Variant!(VariantN, AllowedTypesParam);
180
181 private:
182 // Compute the largest practical size from maxDataSize
183 struct SizeChecker
184 {
185 int function() fptr;
186 ubyte[maxDataSize] data;
187 }
188 enum size = SizeChecker.sizeof - (int function()).sizeof;
189
190 /** Tells whether a type `T` is statically _allowed for
191 * storage inside a `VariantN` object by looking
192 * `T` up in `AllowedTypes`.
193 */
194 public template allowed(T)
195 {
196 enum bool allowed
197 = is(T == VariantN)
198 ||
199 //T.sizeof <= size &&
200 (AllowedTypes.length == 0 || staticIndexOf!(T, AllowedTypes) >= 0);
201 }
202
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 }
208
209 // state
210 union
211 {
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;
216 }
217 ptrdiff_t function(OpID selector, ubyte[size]* store, void* data) fptr
218 = &handler!(void);
219
220 // internals
221 // Handler for an uninitialized value
222 static ptrdiff_t handler(A : void)(OpID selector, ubyte[size]*, void* parm)
223 {
224 switch (selector)
225 {
226 case OpID.getTypeInfo:
227 *cast(TypeInfo *) parm = typeid(A);
228 break;
229 case OpID.copyOut:
230 auto target = cast(VariantN *) parm;
231 target.fptr = &handler!(A);
232 // no need to copy the data (it's garbage)
233 break;
234 case OpID.compare:
235 case OpID.equals:
236 auto rhs = cast(const VariantN *) parm;
237 return rhs.peek!(A)
238 ? 0 // all uninitialized are equal
239 : ptrdiff_t.min; // uninitialized variant is not comparable otherwise
240 case OpID.toString:
241 string * target = cast(string*) parm;
242 *target = "<Uninitialized VariantN>";
243 break;
244 case OpID.postblit:
245 case OpID.destruct:
246 break;
247 case OpID.get:
248 case OpID.testConversion:
249 case OpID.index:
250 case OpID.indexAssign:
251 case OpID.catAssign:
252 case OpID.length:
253 throw new VariantException(
254 "Attempt to use an uninitialized VariantN");
255 default: assert(false, "Invalid OpID");
256 }
257 return 0;
258 }
259
260 // Handler for all of a type's operations
261 static ptrdiff_t handler(A)(OpID selector, ubyte[size]* pStore, void* parm)
262 {
263 import std.conv : to;
264 static A* getPtr(void* untyped)
265 {
266 if (untyped)
267 {
268 static if (A.sizeof <= size)
269 return cast(A*) untyped;
270 else
271 return *cast(A**) untyped;
272 }
273 return null;
274 }
275
276 static ptrdiff_t compare(A* rhsPA, A* zis, OpID selector)
277 {
278 static if (is(typeof(*rhsPA == *zis)))
279 {
280 enum isEmptyStructWithoutOpEquals = is(A == struct) && A.tupleof.length == 0 &&
281 !__traits(hasMember, A, "opEquals");
282 static if (isEmptyStructWithoutOpEquals)
283 {
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
287 return 0;
288 }
289 else
290 {
291 if (*rhsPA == *zis)
292 return 0;
293 static if (is(typeof(*zis < *rhsPA)))
294 {
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;
300 else
301 return ptrdiff_t.min;
302 }
303 else
304 {
305 // Not equal, and type does not support ordering
306 // comparisons.
307 return ptrdiff_t.min;
308 }
309 }
310 }
311 else
312 {
313 // Type does not support comparisons at all.
314 return ptrdiff_t.min;
315 }
316 }
317
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)
324 {
325 alias UA = Unqual!A;
326 static if (isStaticArray!A && is(typeof(UA.init[0])))
327 {
328 alias MutaTypes = AliasSeq!(UA, typeof(UA.init[0])[], AllImplicitConversionTargets!UA);
329 }
330 else
331 {
332 alias MutaTypes = AliasSeq!(UA, AllImplicitConversionTargets!UA);
333 }
334 alias ConstTypes = staticMap!(ConstOf, MutaTypes);
335 alias SharedTypes = staticMap!(SharedOf, MutaTypes);
336 alias SharedConstTypes = staticMap!(SharedConstOf, MutaTypes);
337 alias ImmuTypes = staticMap!(ImmutableOf, MutaTypes);
338
339 static if (is(A == immutable))
340 alias AllTypes = AliasSeq!(ImmuTypes, ConstTypes, SharedConstTypes);
341 else static if (is(A == shared))
342 {
343 static if (is(A == const))
344 alias AllTypes = SharedConstTypes;
345 else
346 alias AllTypes = AliasSeq!(SharedTypes, SharedConstTypes);
347 }
348 else
349 {
350 static if (is(A == const))
351 alias AllTypes = ConstTypes;
352 else
353 alias AllTypes = AliasSeq!(MutaTypes, ConstTypes);
354 }
355
356 foreach (T ; AllTypes)
357 {
358 if (targetType != typeid(T))
359 continue;
360
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))
373 {
374 import core.internal.lifetime : emplaceRef;
375
376 auto zat = cast(T*) target;
377 if (src)
378 {
379 static if (T.sizeof > 0)
380 assert(target, "target must be non-null");
381
382 static if (isStaticArray!A && isDynamicArray!T)
383 {
384 auto this_ = (*src)[];
385 emplaceRef(*cast(Unqual!T*) zat, cast(Unqual!T) this_);
386 }
387 else
388 {
389 emplaceRef(*cast(Unqual!T*) zat, *cast(UA*) src);
390 }
391 }
392 }
393 else
394 {
395 // type T is not constructible from A
396 if (src)
397 assert(false, A.stringof);
398 }
399 return true;
400 }
401 return false;
402 }
403
404 switch (selector)
405 {
406 case OpID.getTypeInfo:
407 *cast(TypeInfo *) parm = typeid(A);
408 break;
409 case OpID.copyOut:
410 auto target = cast(VariantN *) parm;
411 assert(target);
412
413 static if (target.size < A.sizeof)
414 {
415 if (target.type.tsize < A.sizeof)
416 {
417 static if (is(A == U[n], U, size_t n))
418 {
419 A* p = cast(A*)(new U[n]).ptr;
420 }
421 else
422 {
423 A* p = new A;
424 }
425 *cast(A**)&target.store = p;
426 }
427 }
428 tryPutting(zis, typeid(A), cast(void*) getPtr(&target.store))
429 || assert(false);
430 target.fptr = &handler!(A);
431 break;
432 case OpID.get:
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);
437 case OpID.compare:
438 case OpID.equals:
439 auto rhsP = cast(VariantN *) parm;
440 auto rhsType = rhsP.type;
441 // Are we the same?
442 if (rhsType == typeid(A))
443 {
444 // cool! Same type!
445 auto rhsPA = getPtr(&rhsP.store);
446 return compare(rhsPA, zis, selector);
447 }
448 else if (rhsType == typeid(void))
449 {
450 // No support for ordering comparisons with
451 // uninitialized vars
452 return ptrdiff_t.min;
453 }
454 VariantN temp;
455 // Do I convert to rhs?
456 if (tryPutting(zis, rhsType, &temp.store))
457 {
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);
464 else
465 return temp.opEquals(*rhsP) ? 0 : 1;
466 }
467 // Does rhs convert to zis?
468 auto t = tuple(typeid(A), &temp.store);
469 if (rhsP.fptr(OpID.get, &rhsP.store, &t) == 0)
470 {
471 // cool! Now temp has rhs in my type!
472 auto rhsPA = getPtr(&temp.store);
473 return compare(rhsPA, zis, selector);
474 }
475 // Generate the function below only if the Variant's type is
476 // comparable with 'null'
477 static if (__traits(compiles, () => A.init == null))
478 {
479 if (rhsType == typeid(null))
480 {
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
485 // than 'null'
486 return *zis == null ? 0 : 1;
487 }
488 }
489 return ptrdiff_t.min; // dunno
490 case OpID.toString:
491 auto target = cast(string*) parm;
492 static if (is(typeof(to!(string)(*zis))))
493 {
494 *target = to!(string)(*zis);
495 break;
496 }
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();}))
501 {
502 *target = (*zis).toString();
503 break;
504 }
505 else
506 {
507 throw new VariantException(typeid(A), typeid(string));
508 }
509
510 case OpID.index:
511 auto result = cast(Variant*) parm;
512 static if (isArray!(A) && !is(immutable typeof(A.init[0]) == immutable void))
513 {
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];
518 break;
519 }
520 else static if (isAssociativeArray!(A))
521 {
522 *result = (*zis)[result.get!(typeof(A.init.keys[0]))];
523 break;
524 }
525 else
526 {
527 throw new VariantException(typeid(A), result[0].type);
528 }
529
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])))
534 {
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]));
538 break;
539 }
540 else static if (isAssociativeArray!(A) && is(typeof((*zis)[A.init.keys[0]] = A.init.values[0])))
541 {
542 (*zis)[args[1].get!(typeof(A.init.keys[0]))]
543 = args[0].get!(typeof(A.init.values[0]));
544 break;
545 }
546 else
547 {
548 throw new VariantException(typeid(A), args[0].type);
549 }
550
551 case OpID.catAssign:
552 static if (!is(immutable typeof((*zis)[0]) == immutable void) &&
553 is(typeof((*zis)[0])) && is(typeof(*zis ~= *zis)))
554 {
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))
559 {
560 // append one element to the array
561 (*zis) ~= [ arg[0].get!(E) ];
562 }
563 else
564 {
565 // append a whole array to the array
566 (*zis) ~= arg[0].get!(A);
567 }
568 break;
569 }
570 else
571 {
572 throw new VariantException(typeid(A), typeid(void[]));
573 }
574
575 case OpID.length:
576 static if (isArray!(A) || isAssociativeArray!(A))
577 {
578 return zis.length;
579 }
580 else
581 {
582 throw new VariantException(typeid(A), typeid(void[]));
583 }
584
585 case OpID.apply:
586 static if (!isFunctionPointer!A && !isDelegate!A)
587 {
588 import std.conv : text;
589 import std.exception : enforce;
590 enforce(0, text("Cannot apply `()' to a value of type `",
591 A.stringof, "'."));
592 }
593 else
594 {
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)
611 {
612 t[i] = cast() variantArgs[i].get!T;
613 }
614
615 auto args = cast(Tuple!(ParamTypes))t;
616 static if (is(ReturnType!A == void))
617 {
618 (*zis)(args.expand);
619 *p = Variant.init; // void returns uninitialized Variant.
620 }
621 else
622 {
623 *p = (*zis)(args.expand);
624 }
625 }
626 break;
627
628 case OpID.postblit:
629 static if (hasElaborateCopyConstructor!A)
630 {
631 zis.__xpostblit();
632 }
633 break;
634
635 case OpID.destruct:
636 static if (hasElaborateDestructor!A)
637 {
638 zis.__xdtor();
639 }
640 break;
641
642 default: assert(false);
643 }
644 return 0;
645 }
646
647 public:
648 /** Constructs a `VariantN` value given an argument of a
649 * generic type. Statically rejects disallowed types.
650 */
651
652 this(T)(T value)
653 {
654 static assert(allowed!(T), "Cannot store a " ~ T.stringof
655 ~ " in a " ~ VariantN.stringof);
656 opAssign(value);
657 }
658
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))
662 {
663 opAssign(value);
664 }
665
666 static if (!AllowedTypes.length || anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
667 {
668 this(this)
669 {
670 fptr(OpID.postblit, &store, null);
671 }
672 }
673
674 static if (!AllowedTypes.length || anySatisfy!(hasElaborateDestructor, AllowedTypes))
675 {
676 ~this()
677 {
678 // Infer the safety of the provided types
679 static if (AllowedTypes.length)
680 {
681 if (0)
682 {
683 AllowedTypes var;
684 }
685 }
686 (() @trusted => fptr(OpID.destruct, &store, null))();
687 }
688 }
689
690 /** Assigns a `VariantN` from a generic
691 * argument. Statically rejects disallowed types. */
692
693 VariantN opAssign(T)(T rhs)
694 {
695 static assert(allowed!(T), "Cannot store a " ~ T.stringof
696 ~ " in a " ~ VariantN.stringof ~ ". Valid types are "
697 ~ AllowedTypes.stringof);
698
699 static if (is(T : VariantN))
700 {
701 rhs.fptr(OpID.copyOut, &rhs.store, &this);
702 }
703 else static if (is(T : const(VariantN)))
704 {
705 static assert(false,
706 "Assigning Variant objects from const Variant"~
707 " objects is currently not supported.");
708 }
709 else
710 {
711 import core.lifetime : copyEmplace;
712
713 static if (!AllowedTypes.length || anySatisfy!(hasElaborateDestructor, AllowedTypes))
714 {
715 // Assignment should destruct previous value
716 fptr(OpID.destruct, &store, null);
717 }
718
719 static if (T.sizeof <= size)
720 copyEmplace(rhs, *cast(T*) &store);
721 else
722 {
723 static if (is(T == U[n], U, size_t n))
724 auto p = cast(T*) (new U[n]).ptr;
725 else
726 auto p = new T;
727 copyEmplace(rhs, *p);
728 *(cast(T**) &store) = p;
729 }
730
731 fptr = &handler!(T);
732 }
733 return this;
734 }
735
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))
739 {
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);
745 }
746
747
748 Variant opCall(P...)(auto ref P params)
749 {
750 Variant[P.length + 1] pack;
751 pack[0] = P.length;
752 foreach (i, _; params)
753 {
754 pack[i + 1] = params[i];
755 }
756 fptr(OpID.apply, &store, &pack);
757 return pack[0];
758 }
759
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).
763 */
764 @property bool hasValue() const pure nothrow
765 {
766 // @@@BUG@@@ in compiler, the cast shouldn't be needed
767 return cast(typeof(&handler!(void))) fptr != &handler!(void);
768 }
769
770 ///
771 version (StdDdoc)
772 @system unittest
773 {
774 Variant a;
775 assert(!a.hasValue);
776 Variant b;
777 a = b;
778 assert(!a.hasValue); // still no value
779 a = 5;
780 assert(a.hasValue);
781 }
782
783 /**
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.
789 */
790 @property inout(T)* peek(T)() inout
791 {
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))
796 return null;
797 static if (T.sizeof <= size)
798 return cast(inout T*)&store;
799 else
800 return *cast(inout T**)&store;
801 }
802
803 ///
804 version (StdDdoc)
805 @system unittest
806 {
807 Variant a = 5;
808 auto b = a.peek!(int);
809 assert(b !is null);
810 *b = 6;
811 assert(a == 6);
812 }
813
814 /**
815 * Returns the `typeid` of the currently held value.
816 */
817
818 @property TypeInfo type() const nothrow @trusted
819 {
820 scope(failure) assert(0);
821
822 TypeInfo result;
823 fptr(OpID.getTypeInfo, null, &result);
824 return result;
825 }
826
827 /**
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).
832 */
833
834 @property bool convertsTo(T)() const
835 {
836 TypeInfo info = typeid(T);
837 return fptr(OpID.testConversion, null, &info) == 0;
838 }
839
840 /**
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)).
844
845 Params:
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.
850 */
851 @property inout(T) get(T)() inout
852 {
853 inout(T) result = void;
854 static if (is(T == shared))
855 alias R = shared Unqual!T;
856 else
857 alias R = Unqual!T;
858 auto buf = tuple(typeid(T), cast(R*)&result);
859
860 if (fptr(OpID.get, cast(ubyte[size]*) &store, &buf))
861 {
862 throw new VariantException(type, typeid(T));
863 }
864 return result;
865 }
866
867 /// Ditto
868 @property auto get(uint index)() inout
869 if (index < AllowedTypes.length)
870 {
871 foreach (i, T; AllowedTypes)
872 {
873 static if (index == i) return get!T;
874 }
875 assert(0);
876 }
877
878 /**
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
885 * VariantException).
886 */
887
888 @property T coerce(T)()
889 {
890 import std.conv : to, text;
891 static if (isNumeric!T || isBoolean!T)
892 {
893 if (convertsTo!real)
894 {
895 // maybe optimize this fella; handle ints separately
896 return to!T(get!real);
897 }
898 else if (convertsTo!(const(char)[]))
899 {
900 return to!T(get!(const(char)[]));
901 }
902 // I'm not sure why this doesn't convert to const(char),
903 // but apparently it doesn't (probably a deeper bug).
904 //
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)[]))
908 {
909 return to!T(get!(immutable(char)[]));
910 }
911 else
912 {
913 import std.exception : enforce;
914 enforce(false, text("Type ", type, " does not convert to ",
915 typeid(T)));
916 assert(0);
917 }
918 }
919 else static if (is(T : Object))
920 {
921 return to!(T)(get!(Object));
922 }
923 else static if (isSomeString!(T))
924 {
925 return to!(T)(toString());
926 }
927 else
928 {
929 // Fix for bug 1649
930 static assert(false, "unsupported type for coercion");
931 }
932 }
933
934 /**
935 * Formats the stored value as a string.
936 */
937
938 string toString()
939 {
940 string result;
941 fptr(OpID.toString, &store, &result) == 0 || assert(false);
942 return result;
943 }
944
945 /**
946 * Comparison for equality used by the "==" and "!=" operators.
947 */
948
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))
952 {
953 static if (is(immutable T == immutable VariantN))
954 alias temp = rhs;
955 else
956 auto temp = VariantN(rhs);
957 return !fptr(OpID.equals, cast(ubyte[size]*) &store,
958 cast(void*) &temp);
959 }
960
961 // workaround for bug 10567 fix
962 int opCmp(ref const VariantN rhs) const
963 {
964 return (cast() this).opCmp!(VariantN)(cast() rhs);
965 }
966
967 /**
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.
971 */
972
973 int opCmp(T)(T rhs)
974 if (allowed!T) // includes T == VariantN
975 {
976 static if (is(T == VariantN))
977 alias temp = rhs;
978 else
979 auto temp = VariantN(rhs);
980 auto result = fptr(OpID.compare, &store, &temp);
981 if (result == ptrdiff_t.min)
982 {
983 throw new VariantException(type, temp.type);
984 }
985
986 assert(result >= -1 && result <= 1); // Should be true for opCmp.
987 return cast(int) result;
988 }
989
990 /**
991 * Computes the hash of the held value.
992 */
993
994 size_t toHash() const nothrow @safe
995 {
996 return type.getHash(&store);
997 }
998
999 private VariantN opArithmetic(T, string op)(T other)
1000 {
1001 static if (isInstanceOf!(.VariantN, T))
1002 {
1003 string tryUseType(string tp)
1004 {
1005 import std.format : format;
1006 return q{
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);
1010 }.format(tp, op);
1011 }
1012
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"));
1020 }
1021 else
1022 {
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"));
1039 }
1040
1041 throw new VariantException("No possible match found for VariantN "~op~" "~T.stringof);
1042 }
1043
1044 private VariantN opLogic(T, string op)(T other)
1045 {
1046 VariantN result;
1047 static if (is(T == VariantN))
1048 {
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)");
1055 else
1056 result = mixin("get!(long) " ~ op ~ " other.get!(long)");
1057 }
1058 else
1059 {
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");
1067 else
1068 result = mixin("get!(long) " ~ op ~ " other");
1069 }
1070 return result;
1071 }
1072
1073 /**
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.
1079 */
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); }
1084 ///ditto
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); }
1089 ///ditto
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); }
1094 ///ditto
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); }
1099 ///ditto
1100 VariantN opBinary(string op, T)(T rhs)
1101 if (op == "~")
1102 {
1103 auto temp = this;
1104 temp ~= rhs;
1105 return temp;
1106 }
1107 // ///ditto
1108 // VariantN opBinaryRight(string op, T)(T rhs)
1109 // if (op == "~")
1110 // {
1111 // VariantN temp = rhs;
1112 // temp ~= this;
1113 // return temp;
1114 // }
1115
1116 ///ditto
1117 VariantN opOpAssign(string op, T)(T rhs)
1118 {
1119 static if (op != "~")
1120 {
1121 mixin("return this = this" ~ op ~ "rhs;");
1122 }
1123 else
1124 {
1125 auto toAppend = Variant(rhs);
1126 fptr(OpID.catAssign, &store, &toAppend) == 0 || assert(false);
1127 return this;
1128 }
1129 }
1130
1131 /**
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.
1135 */
1136 inout(Variant) opIndex(K)(K i) inout
1137 {
1138 auto result = Variant(i);
1139 fptr(OpID.index, cast(ubyte[size]*) &store, &result) == 0 || assert(false);
1140 return result;
1141 }
1142
1143 ///
1144 version (StdDdoc)
1145 @system unittest
1146 {
1147 Variant a = new int[10];
1148 a[5] = 42;
1149 assert(a[5] == 42);
1150 a[5] += 8;
1151 assert(a[5] == 50);
1152
1153 int[int] hash = [ 42:24 ];
1154 a = hash;
1155 assert(a[42] == 24);
1156 a[42] /= 2;
1157 assert(a[42] == 12);
1158 }
1159
1160 /// ditto
1161 Variant opIndexAssign(T, N)(T value, N i)
1162 {
1163 static if (AllowedTypes.length && !isInstanceOf!(.VariantN, T))
1164 {
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);
1169 }
1170 Variant[2] args = [ Variant(value), Variant(i) ];
1171 fptr(OpID.indexAssign, &store, &args) == 0 || assert(false);
1172 return args[0];
1173 }
1174
1175 /// ditto
1176 Variant opIndexOpAssign(string op, T, N)(T value, N i)
1177 {
1178 return opIndexAssign(mixin(`opIndex(i)` ~ op ~ `value`), i);
1179 }
1180
1181 /** If the `VariantN` contains an (associative) array,
1182 * returns the _length of that array. Otherwise, throws an
1183 * exception.
1184 */
1185 @property size_t length()
1186 {
1187 return cast(size_t) fptr(OpID.length, &store, null);
1188 }
1189
1190 /**
1191 If the `VariantN` contains an array, applies `dg` to each
1192 element of the array in turn. Otherwise, throws an exception.
1193 */
1194 int opApply(Delegate)(scope Delegate dg) if (is(Delegate == delegate))
1195 {
1196 alias A = Parameters!(Delegate)[0];
1197 if (type == typeid(A[]))
1198 {
1199 auto arr = get!(A[]);
1200 foreach (ref e; arr)
1201 {
1202 if (dg(e)) return 1;
1203 }
1204 }
1205 else static if (is(A == VariantN))
1206 {
1207 foreach (i; 0 .. length)
1208 {
1209 // @@@TODO@@@: find a better way to not confuse
1210 // clients who think they change values stored in the
1211 // Variant when in fact they are only changing tmp.
1212 auto tmp = this[i];
1213 debug scope(exit) assert(tmp == this[i]);
1214 if (dg(tmp)) return 1;
1215 }
1216 }
1217 else
1218 {
1219 import std.conv : text;
1220 import std.exception : enforce;
1221 enforce(false, text("Variant type ", type,
1222 " not iterable with values of type ",
1223 A.stringof));
1224 }
1225 return 0;
1226 }
1227 }
1228
1229 ///
1230 @system unittest
1231 {
1232 alias Var = VariantN!(maxSize!(int, double, string));
1233
1234 Var a; // Must assign before use, otherwise exception ensues
1235 // Initialize with an integer; make the type int
1236 Var b = 42;
1237 assert(b.type == typeid(int));
1238 // Peek at the value
1239 assert(b.peek!(int) !is null && *b.peek!(int) == 42);
1240 // Automatically convert per language rules
1241 auto x = b.get!(real);
1242
1243 // Assign any other type, including other variants
1244 a = b;
1245 a = 3.14;
1246 assert(a.type == typeid(double));
1247 // Implicit conversions work just as with built-in types
1248 assert(a < b);
1249 // Check for convertibility
1250 assert(!a.convertsTo!(int)); // double not convertible to int
1251 // Strings and all other arrays are supported
1252 a = "now I'm a string";
1253 assert(a == "now I'm a string");
1254 }
1255
1256 /// can also assign arrays
1257 @system unittest
1258 {
1259 alias Var = VariantN!(maxSize!(int[]));
1260
1261 Var a = new int[42];
1262 assert(a.length == 42);
1263 a[5] = 7;
1264 assert(a[5] == 7);
1265 }
1266
1267 @safe unittest
1268 {
1269 alias V = VariantN!24;
1270 const alignMask = V.alignof - 1;
1271 assert(V.sizeof == ((24 + (void*).sizeof + alignMask) & ~alignMask));
1272 }
1273
1274 /// Can also assign class values
1275 @system unittest
1276 {
1277 alias Var = VariantN!(maxSize!(int*)); // classes are pointers
1278 Var a;
1279
1280 class Foo {}
1281 auto foo = new Foo;
1282 a = foo;
1283 assert(*a.peek!(Foo) == foo); // and full type information is preserved
1284 }
1285
1286 @system unittest
1287 {
1288 import std.conv : to;
1289 Variant v;
foo()1290 int foo() { return 42; }
1291 v = &foo;
1292 assert(v() == 42);
1293
bar(string s)1294 static int bar(string s) { return to!int(s); }
1295 v = &bar;
1296 assert(v("43") == 43);
1297 }
1298
1299 @system unittest
1300 {
1301 int[int] hash = [ 42:24 ];
1302 Variant v = hash;
1303 assert(v[42] == 24);
1304 v[42] = 5;
1305 assert(v[42] == 5);
1306 }
1307
1308 // opIndex with static arrays, https://issues.dlang.org/show_bug.cgi?id=12771
1309 @system unittest
1310 {
1311 int[4] elements = [0, 1, 2, 3];
1312 Variant v = elements;
1313 assert(v == elements);
1314 assert(v[2] == 2);
1315 assert(v[3] == 3);
1316 v[2] = 6;
1317 assert(v[2] == 6);
1318 assert(v != elements);
1319 }
1320
1321 @system unittest
1322 {
1323 import std.exception : assertThrown;
1324 Algebraic!(int[]) v = [2, 2];
1325
1326 assert(v == [2, 2]);
1327 v[0] = 1;
1328 assert(v[0] == 1);
1329 assert(v != [2, 2]);
1330
1331 // opIndexAssign from Variant
1332 v[1] = v[0];
1333 assert(v[1] == 1);
1334
1335 static assert(!__traits(compiles, (v[1] = null)));
1336 assertThrown!VariantException(v[1] = Variant(null));
1337 }
1338
1339 // https://issues.dlang.org/show_bug.cgi?id=10879
1340 @system unittest
1341 {
1342 int[10] arr = [1,2,3,4,5,6,7,8,9,10];
1343 Variant v1 = arr;
1344 Variant v2;
1345 v2 = arr;
1346 assert(v1 == arr);
1347 assert(v2 == arr);
foreach(i,e;arr)1348 foreach (i, e; arr)
1349 {
1350 assert(v1[i] == e);
1351 assert(v2[i] == e);
1352 }
1353 static struct LargeStruct
1354 {
1355 int[100] data;
1356 }
1357 LargeStruct ls;
1358 ls.data[] = 4;
1359 v1 = ls;
1360 Variant v3 = ls;
1361 assert(v1 == ls);
1362 assert(v3 == ls);
1363 }
1364
1365 // https://issues.dlang.org/show_bug.cgi?id=8195
1366 @system unittest
1367 {
1368 struct S
1369 {
1370 int a;
1371 long b;
1372 string c;
1373 real d = 0.0;
1374 bool e;
1375 }
1376
1377 static assert(S.sizeof >= Variant.sizeof);
1378 alias Types = AliasSeq!(string, int, S);
1379 alias MyVariant = VariantN!(maxSize!Types, Types);
1380
1381 auto v = MyVariant(S.init);
1382 assert(v == S.init);
1383 }
1384
1385 // https://issues.dlang.org/show_bug.cgi?id=10961
1386 @system unittest
1387 {
1388 // Primarily test that we can assign a void[] to a Variant.
1389 void[] elements = cast(void[])[1, 2, 3];
1390 Variant v = elements;
1391 void[] returned = v.get!(void[]);
1392 assert(returned == elements);
1393 }
1394
1395 // https://issues.dlang.org/show_bug.cgi?id=13352
1396 @system unittest
1397 {
1398 alias TP = Algebraic!(long);
1399 auto a = TP(1L);
1400 auto b = TP(2L);
1401 assert(!TP.allowed!ulong);
1402 assert(a + b == 3L);
1403 assert(a + 2 == 3L);
1404 assert(1 + b == 3L);
1405
1406 alias TP2 = Algebraic!(long, string);
1407 auto c = TP2(3L);
1408 assert(a + c == 4L);
1409 }
1410
1411 // https://issues.dlang.org/show_bug.cgi?id=13354
1412 @system unittest
1413 {
1414 alias A = Algebraic!(string[]);
1415 A a = ["a", "b"];
1416 assert(a[0] == "a");
1417 assert(a[1] == "b");
1418 a[1] = "c";
1419 assert(a[1] == "c");
1420
1421 alias AA = Algebraic!(int[string]);
1422 AA aa = ["a": 1, "b": 2];
1423 assert(aa["a"] == 1);
1424 assert(aa["b"] == 2);
1425 aa["b"] = 3;
1426 assert(aa["b"] == 3);
1427 }
1428
1429 // https://issues.dlang.org/show_bug.cgi?id=14198
1430 @system unittest
1431 {
1432 Variant a = true;
1433 assert(a.type == typeid(bool));
1434 }
1435
1436 // https://issues.dlang.org/show_bug.cgi?id=14233
1437 @system unittest
1438 {
1439 alias Atom = Algebraic!(string, This[]);
1440
1441 Atom[] values = [];
1442 auto a = Atom(values);
1443 }
1444
1445 pure nothrow @nogc
1446 @system unittest
1447 {
1448 Algebraic!(int, double) a;
1449 a = 100;
1450 a = 1.0;
1451 }
1452
1453 // https://issues.dlang.org/show_bug.cgi?id=14457
1454 @system unittest
1455 {
1456 alias A = Algebraic!(int, float, double);
1457 alias B = Algebraic!(int, float);
1458
1459 A a = 1;
1460 B b = 6f;
1461 a = b;
1462
1463 assert(a.type == typeid(float));
1464 assert(a.get!float == 6f);
1465 }
1466
1467 // https://issues.dlang.org/show_bug.cgi?id=14585
1468 @system unittest
1469 {
1470 static struct S
1471 {
1472 int x = 42;
~thisS1473 ~this() {assert(x == 42);}
1474 }
1475 Variant(S()).get!S;
1476 }
1477
1478 // https://issues.dlang.org/show_bug.cgi?id=14586
1479 @system unittest
1480 {
1481 const Variant v = new immutable Object;
1482 v.get!(immutable Object);
1483 }
1484
1485 @system unittest
1486 {
1487 static struct S
1488 {
opCastS1489 T opCast(T)() {assert(false);}
1490 }
1491 Variant v = S();
1492 v.get!S;
1493 }
1494
1495 // https://issues.dlang.org/show_bug.cgi?id=13262
1496 @system unittest
1497 {
fun(T)1498 static void fun(T)(Variant v){
1499 T x;
1500 v = x;
1501 auto r = v.get!(T);
1502 }
1503 Variant v;
1504 fun!(shared(int))(v);
1505 fun!(shared(int)[])(v);
1506
1507 static struct S1
1508 {
1509 int c;
1510 string a;
1511 }
1512
1513 static struct S2
1514 {
1515 string a;
1516 shared int[] b;
1517 }
1518
1519 static struct S3
1520 {
1521 string a;
1522 shared int[] b;
1523 int c;
1524 }
1525
1526 fun!(S1)(v);
1527 fun!(shared(S1))(v);
1528 fun!(S2)(v);
1529 fun!(shared(S2))(v);
1530 fun!(S3)(v);
1531 fun!(shared(S3))(v);
1532
1533 // ensure structs that are shared, but don't have shared postblits
1534 // can't be used.
1535 static struct S4
1536 {
1537 int x;
thisS41538 this(this) {x = 0;}
1539 }
1540
1541 fun!(S4)(v);
1542 static assert(!is(typeof(fun!(shared(S4))(v))));
1543 }
1544
1545 @safe unittest
1546 {
1547 Algebraic!(int) x;
1548
1549 static struct SafeS
1550 {
~thisSafeS1551 @safe ~this() {}
1552 }
1553
1554 Algebraic!(SafeS) y;
1555 }
1556
1557 // https://issues.dlang.org/show_bug.cgi?id=19986
1558 @system unittest
1559 {
1560 VariantN!32 v;
1561 v = const(ubyte[33]).init;
1562
1563 struct S
1564 {
1565 ubyte[33] s;
1566 }
1567
1568 VariantN!32 v2;
1569 v2 = const(S).init;
1570 }
1571
1572 // https://issues.dlang.org/show_bug.cgi?id=21021
1573 @system unittest
1574 {
1575 static struct S
1576 {
1577 int h;
1578 int[5] array;
1579 alias h this;
1580 }
1581
1582 S msg;
1583 msg.array[] = 3;
1584 Variant a = msg;
1585 auto other = a.get!S;
1586 assert(msg.array[0] == 3);
1587 assert(other.array[0] == 3);
1588 }
1589
1590 // https://issues.dlang.org/show_bug.cgi?id=21231
1591 // Compatibility with -preview=fieldwise
1592 @system unittest
1593 {
1594 static struct Empty
1595 {
opCmpEmpty1596 bool opCmp(const scope ref Empty) const
1597 { return false; }
1598 }
1599
1600 Empty a, b;
1601 assert(a == b);
1602 assert(!(a < b));
1603
1604 VariantN!(4, Empty) v = a;
1605 assert(v == b);
1606 assert(!(v < b));
1607 }
1608
1609 // Compatibility with -preview=fieldwise
1610 @system unittest
1611 {
1612 static struct Empty
1613 {
opEqualsEmpty1614 bool opEquals(const scope ref Empty) const
1615 { return false; }
1616 }
1617
1618 Empty a, b;
1619 assert(a != b);
1620
1621 VariantN!(4, Empty) v = a;
1622 assert(v != b);
1623 }
1624
1625 // https://issues.dlang.org/show_bug.cgi?id=22647
1626 // Can compare with 'null'
1627 @system unittest
1628 {
1629 static struct Bar
1630 {
1631 int* ptr;
1632 alias ptr this;
1633 }
1634
1635 static class Foo {}
1636 int* iptr;
1637 int[] arr;
1638
1639 Variant v = Foo.init; // 'null'
1640 assert(v != null); // can only compare objects with 'null' by using 'is'
1641
1642 v = iptr;
1643 assert(v == null); // pointers can be compared with 'null'
1644
1645 v = arr;
1646 assert(v == null); // arrays can be compared with 'null'
1647
1648 v = "";
1649 assert(v == null); // strings are arrays, an empty string is considered 'null'
1650
1651 v = Bar.init;
1652 assert(v == null); // works with alias this
1653
1654 v = [3];
1655 assert(v != null);
1656 assert(v > null);
1657 assert(v >= null);
1658 assert(!(v < null));
1659 }
1660
1661 /**
1662 _Algebraic data type restricted to a closed set of possible
1663 types. It's an alias for $(LREF VariantN) with an
1664 appropriately-constructed maximum size. `Algebraic` is
1665 useful when it is desirable to restrict what a discriminated type
1666 could hold to the end of defining simpler and more efficient
1667 manipulation.
1668
1669 $(RED Warning: $(LREF Algebraic) is outdated and not recommended for use in new
1670 code. Instead, use $(REF SumType, std,sumtype).)
1671 */
Algebraic(T...)1672 template Algebraic(T...)
1673 {
1674 alias Algebraic = VariantN!(maxSize!T, T);
1675 }
1676
1677 ///
1678 @system unittest
1679 {
1680 auto v = Algebraic!(int, double, string)(5);
1681 assert(v.peek!(int));
1682 v = 3.14;
1683 assert(v.peek!(double));
1684 // auto x = v.peek!(long); // won't compile, type long not allowed
1685 // v = '1'; // won't compile, type char not allowed
1686 }
1687
1688 /**
1689 $(H4 Self-Referential Types)
1690
1691 A useful and popular use of algebraic data structures is for defining $(LUCKY
1692 self-referential data structures), i.e. structures that embed references to
1693 values of their own type within.
1694
1695 This is achieved with `Algebraic` by using `This` as a placeholder whenever a
1696 reference to the type being defined is needed. The `Algebraic` instantiation
1697 will perform $(LINK2 https://en.wikipedia.org/wiki/Name_resolution_(programming_languages)#Alpha_renaming_to_make_name_resolution_trivial,
1698 alpha renaming) on its constituent types, replacing `This`
1699 with the self-referenced type. The structure of the type involving `This` may
1700 be arbitrarily complex.
1701 */
1702 @system unittest
1703 {
1704 import std.typecons : Tuple, tuple;
1705
1706 // A tree is either a leaf or a branch of two other trees
1707 alias Tree(Leaf) = Algebraic!(Leaf, Tuple!(This*, This*));
1708 Tree!int tree = tuple(new Tree!int(42), new Tree!int(43));
1709 Tree!int* right = tree.get!1[1];
1710 assert(*right == 43);
1711
1712 // An object is a double, a string, or a hash of objects
1713 alias Obj = Algebraic!(double, string, This[string]);
1714 Obj obj = "hello";
1715 assert(obj.get!1 == "hello");
1716 obj = 42.0;
1717 assert(obj.get!0 == 42);
1718 obj = ["customer": Obj("John"), "paid": Obj(23.95)];
1719 assert(obj.get!2["customer"] == "John");
1720 }
1721
1722 private struct FakeComplexReal
1723 {
1724 real re, im;
1725 }
1726
1727 /**
1728 Alias for $(LREF VariantN) instantiated with the largest size of `creal`,
1729 `char[]`, and `void delegate()`. This ensures that `Variant` is large enough
1730 to hold all of D's predefined types unboxed, including all numeric types,
1731 pointers, delegates, and class references. You may want to use
1732 `VariantN` directly with a different maximum size either for
1733 storing larger types unboxed, or for saving memory.
1734 */
1735 alias Variant = VariantN!(maxSize!(FakeComplexReal, char[], void delegate()));
1736
1737 ///
1738 @system unittest
1739 {
1740 Variant a; // Must assign before use, otherwise exception ensues
1741 // Initialize with an integer; make the type int
1742 Variant b = 42;
1743 assert(b.type == typeid(int));
1744 // Peek at the value
1745 assert(b.peek!(int) !is null && *b.peek!(int) == 42);
1746 // Automatically convert per language rules
1747 auto x = b.get!(real);
1748
1749 // Assign any other type, including other variants
1750 a = b;
1751 a = 3.14;
1752 assert(a.type == typeid(double));
1753 // Implicit conversions work just as with built-in types
1754 assert(a < b);
1755 // Check for convertibility
1756 assert(!a.convertsTo!(int)); // double not convertible to int
1757 // Strings and all other arrays are supported
1758 a = "now I'm a string";
1759 assert(a == "now I'm a string");
1760 }
1761
1762 /// can also assign arrays
1763 @system unittest
1764 {
1765 Variant a = new int[42];
1766 assert(a.length == 42);
1767 a[5] = 7;
1768 assert(a[5] == 7);
1769 }
1770
1771 /// Can also assign class values
1772 @system unittest
1773 {
1774 Variant a;
1775
1776 class Foo {}
1777 auto foo = new Foo;
1778 a = foo;
1779 assert(*a.peek!(Foo) == foo); // and full type information is preserved
1780 }
1781
1782 /**
1783 * Returns an array of variants constructed from `args`.
1784 *
1785 * This is by design. During construction the `Variant` needs
1786 * static type information about the type being held, so as to store a
1787 * pointer to function for fast retrieval.
1788 */
variantArray(T...)1789 Variant[] variantArray(T...)(T args)
1790 {
1791 Variant[] result;
1792 foreach (arg; args)
1793 {
1794 result ~= Variant(arg);
1795 }
1796 return result;
1797 }
1798
1799 ///
1800 @system unittest
1801 {
1802 auto a = variantArray(1, 3.14, "Hi!");
1803 assert(a[1] == 3.14);
1804 auto b = Variant(a); // variant array as variant
1805 assert(b[1] == 3.14);
1806 }
1807
1808 /**
1809 * Thrown in three cases:
1810 *
1811 * $(OL $(LI An uninitialized `Variant` is used in any way except
1812 * assignment and `hasValue`;) $(LI A `get` or
1813 * `coerce` is attempted with an incompatible target type;)
1814 * $(LI A comparison between `Variant` objects of
1815 * incompatible types is attempted.))
1816 *
1817 */
1818
1819 // @@@ BUG IN COMPILER. THE 'STATIC' BELOW SHOULD NOT COMPILE
1820 static class VariantException : Exception
1821 {
1822 /// The source type in the conversion or comparison
1823 TypeInfo source;
1824 /// The target type in the conversion or comparison
1825 TypeInfo target;
this(string s)1826 this(string s)
1827 {
1828 super(s);
1829 }
this(TypeInfo source,TypeInfo target)1830 this(TypeInfo source, TypeInfo target)
1831 {
1832 super("Variant: attempting to use incompatible types "
1833 ~ source.toString()
1834 ~ " and " ~ target.toString());
1835 this.source = source;
1836 this.target = target;
1837 }
1838 }
1839
1840 ///
1841 @system unittest
1842 {
1843 import std.exception : assertThrown;
1844
1845 Variant v;
1846
1847 // uninitialized use
1848 assertThrown!VariantException(v + 1);
1849 assertThrown!VariantException(v.length);
1850
1851 // .get with an incompatible target type
1852 assertThrown!VariantException(Variant("a").get!int);
1853
1854 // comparison between incompatible types
1855 assertThrown!VariantException(Variant(3) < Variant("a"));
1856 }
1857
1858 @system unittest
1859 {
1860 alias W1 = This2Variant!(char, int, This[int]);
1861 alias W2 = AliasSeq!(int, char[int]);
1862 static assert(is(W1 == W2));
1863
1864 alias var_t = Algebraic!(void, string);
1865 var_t foo = "quux";
1866 }
1867
1868 @system unittest
1869 {
1870 alias A = Algebraic!(real, This[], This[int], This[This]);
1871 A v1, v2, v3;
1872 v2 = 5.0L;
1873 v3 = 42.0L;
1874 //v1 = [ v2 ][];
1875 auto v = v1.peek!(A[]);
1876 //writeln(v[0]);
1877 v1 = [ 9 : v3 ];
1878 //writeln(v1);
1879 v1 = [ v3 : v3 ];
1880 //writeln(v1);
1881 }
1882
1883 @system unittest
1884 {
1885 import std.conv : ConvException;
1886 import std.exception : assertThrown, collectException;
1887 // try it with an oddly small size
1888 VariantN!(1) test;
1889 assert(test.size > 1);
1890
1891 // variantArray tests
1892 auto heterogeneous = variantArray(1, 4.5, "hi");
1893 assert(heterogeneous.length == 3);
1894 auto variantArrayAsVariant = Variant(heterogeneous);
1895 assert(variantArrayAsVariant[0] == 1);
1896 assert(variantArrayAsVariant.length == 3);
1897
1898 // array tests
1899 auto arr = Variant([1.2].dup);
1900 auto e = arr[0];
1901 assert(e == 1.2);
1902 arr[0] = 2.0;
1903 assert(arr[0] == 2);
1904 arr ~= 4.5;
1905 assert(arr[1] == 4.5);
1906
1907 // general tests
1908 Variant a;
1909 auto b = Variant(5);
1910 assert(!b.peek!(real) && b.peek!(int));
1911 // assign
1912 a = *b.peek!(int);
1913 // comparison
1914 assert(a == b, a.type.toString() ~ " " ~ b.type.toString());
1915 auto c = Variant("this is a string");
1916 assert(a != c);
1917 // comparison via implicit conversions
1918 a = 42; b = 42.0; assert(a == b);
1919
1920 // try failing conversions
1921 bool failed = false;
1922 try
1923 {
1924 auto d = c.get!(int);
1925 }
catch(Exception e)1926 catch (Exception e)
1927 {
1928 //writeln(stderr, e.toString);
1929 failed = true;
1930 }
1931 assert(failed); // :o)
1932
1933 // toString tests
1934 a = Variant(42); assert(a.toString() == "42");
1935 a = Variant(42.22); assert(a.toString() == "42.22");
1936
1937 // coerce tests
1938 a = Variant(42.22); assert(a.coerce!(int) == 42);
1939 a = cast(short) 5; assert(a.coerce!(double) == 5);
1940 a = Variant("10"); assert(a.coerce!int == 10);
1941
1942 a = Variant(1);
1943 assert(a.coerce!bool);
1944 a = Variant(0);
1945 assert(!a.coerce!bool);
1946
1947 a = Variant(1.0);
1948 assert(a.coerce!bool);
1949 a = Variant(0.0);
1950 assert(!a.coerce!bool);
1951 a = Variant(float.init);
1952 assertThrown!ConvException(a.coerce!bool);
1953
1954 a = Variant("true");
1955 assert(a.coerce!bool);
1956 a = Variant("false");
1957 assert(!a.coerce!bool);
1958 a = Variant("");
1959 assertThrown!ConvException(a.coerce!bool);
1960
1961 // Object tests
1962 class B1 {}
1963 class B2 : B1 {}
1964 a = new B2;
1965 assert(a.coerce!(B1) !is null);
1966 a = new B1;
1967 assert(collectException(a.coerce!(B2) is null));
1968 a = cast(Object) new B2; // lose static type info; should still work
1969 assert(a.coerce!(B2) !is null);
1970
1971 // struct Big { int a[45]; }
1972 // a = Big.init;
1973
1974 // hash
1975 assert(a.toHash() != 0);
1976 }
1977
1978 // tests adapted from
1979 // http://www.dsource.org/projects/tango/browser/trunk/tango/core/Variant.d?rev=2601
1980 @system unittest
1981 {
1982 Variant v;
1983
1984 assert(!v.hasValue);
1985 v = 42;
1986 assert( v.peek!(int) );
1987 assert( v.convertsTo!(long) );
1988 assert( v.get!(int) == 42 );
1989 assert( v.get!(long) == 42L );
1990 assert( v.get!(ulong) == 42uL );
1991
1992 v = "Hello, World!";
1993 assert( v.peek!(string) );
1994
1995 assert( v.get!(string) == "Hello, World!" );
1996 assert(!is(char[] : wchar[]));
1997 assert( !v.convertsTo!(wchar[]) );
1998 assert( v.get!(string) == "Hello, World!" );
1999
2000 // Literal arrays are dynamically-typed
2001 v = cast(int[4]) [1,2,3,4];
2002 assert( v.peek!(int[4]) );
2003 assert( v.get!(int[4]) == [1,2,3,4] );
2004
2005 {
2006 v = [1,2,3,4,5];
2007 assert( v.peek!(int[]) );
2008 assert( v.get!(int[]) == [1,2,3,4,5] );
2009 }
2010
2011 v = 3.1413;
2012 assert( v.peek!(double) );
2013 assert( v.convertsTo!(real) );
2014 //@@@ BUG IN COMPILER: DOUBLE SHOULD NOT IMPLICITLY CONVERT TO FLOAT
2015 assert( v.convertsTo!(float) );
2016 assert( *v.peek!(double) == 3.1413 );
2017
2018 auto u = Variant(v);
2019 assert( u.peek!(double) );
2020 assert( *u.peek!(double) == 3.1413 );
2021
2022 // operators
2023 v = 38;
2024 assert( v + 4 == 42 );
2025 assert( 4 + v == 42 );
2026 assert( v - 4 == 34 );
2027 assert( Variant(4) - v == -34 );
2028 assert( v * 2 == 76 );
2029 assert( 2 * v == 76 );
2030 assert( v / 2 == 19 );
2031 assert( Variant(2) / v == 0 );
2032 assert( v % 2 == 0 );
2033 assert( Variant(2) % v == 2 );
2034 assert( (v & 6) == 6 );
2035 assert( (6 & v) == 6 );
2036 assert( (v | 9) == 47 );
2037 assert( (9 | v) == 47 );
2038 assert( (v ^ 5) == 35 );
2039 assert( (5 ^ v) == 35 );
2040 assert( v << 1 == 76 );
2041 assert( Variant(1) << Variant(2) == 4 );
2042 assert( v >> 1 == 19 );
2043 assert( Variant(4) >> Variant(2) == 1 );
2044 assert( Variant("abc") ~ "def" == "abcdef" );
2045 assert( Variant("abc") ~ Variant("def") == "abcdef" );
2046
2047 v = 38;
2048 v += 4;
2049 assert( v == 42 );
2050 v = 38; v -= 4; assert( v == 34 );
2051 v = 38; v *= 2; assert( v == 76 );
2052 v = 38; v /= 2; assert( v == 19 );
2053 v = 38; v %= 2; assert( v == 0 );
2054 v = 38; v &= 6; assert( v == 6 );
2055 v = 38; v |= 9; assert( v == 47 );
2056 v = 38; v ^= 5; assert( v == 35 );
2057 v = 38; v <<= 1; assert( v == 76 );
2058 v = 38; v >>= 1; assert( v == 19 );
2059 v = 38; v += 1; assert( v < 40 );
2060
2061 v = "abc";
2062 v ~= "def";
2063 assert( v == "abcdef", *v.peek!(char[]) );
2064 assert( Variant(0) < Variant(42) );
2065 assert( Variant(42) > Variant(0) );
2066 assert( Variant(42) > Variant(0.1) );
2067 assert( Variant(42.1) > Variant(1) );
2068 assert( Variant(21) == Variant(21) );
2069 assert( Variant(0) != Variant(42) );
2070 assert( Variant("bar") == Variant("bar") );
2071 assert( Variant("foo") != Variant("bar") );
2072
2073 {
2074 auto v1 = Variant(42);
2075 auto v2 = Variant("foo");
2076
2077 int[Variant] hash;
2078 hash[v1] = 0;
2079 hash[v2] = 1;
2080
2081 assert( hash[v1] == 0 );
2082 assert( hash[v2] == 1 );
2083 }
2084
2085 {
2086 int[char[]] hash;
2087 hash["a"] = 1;
2088 hash["b"] = 2;
2089 hash["c"] = 3;
2090 Variant vhash = hash;
2091
2092 assert( vhash.get!(int[char[]])["a"] == 1 );
2093 assert( vhash.get!(int[char[]])["b"] == 2 );
2094 assert( vhash.get!(int[char[]])["c"] == 3 );
2095 }
2096 }
2097
2098 @system unittest
2099 {
2100 // check comparisons incompatible with AllowedTypes
2101 Algebraic!int v = 2;
2102
2103 assert(v == 2);
2104 assert(v < 3);
2105 static assert(!__traits(compiles, () => v == long.max));
2106 static assert(!__traits(compiles, () => v == null));
2107 static assert(!__traits(compiles, () => v < long.max));
2108 static assert(!__traits(compiles, () => v > null));
2109 }
2110
2111 // https://issues.dlang.org/show_bug.cgi?id=1558
2112 @system unittest
2113 {
2114 Variant va=1;
2115 Variant vb=-2;
2116 assert((va+vb).get!(int) == -1);
2117 assert((va-vb).get!(int) == 3);
2118 }
2119
2120 @system unittest
2121 {
2122 Variant a;
2123 a=5;
2124 Variant b;
2125 b=a;
2126 Variant[] c;
2127 c = variantArray(1, 2, 3.0, "hello", 4);
2128 assert(c[3] == "hello");
2129 }
2130
2131 @system unittest
2132 {
2133 Variant v = 5;
2134 assert(!__traits(compiles, v.coerce!(bool delegate())));
2135 }
2136
2137
2138 @system unittest
2139 {
2140 struct Huge {
2141 real a, b, c, d, e, f, g;
2142 }
2143
2144 Huge huge;
2145 huge.e = 42;
2146 Variant v;
2147 v = huge; // Compile time error.
2148 assert(v.get!(Huge).e == 42);
2149 }
2150
2151 @system unittest
2152 {
2153 const x = Variant(42);
2154 auto y1 = x.get!(const int);
2155 // @@@BUG@@@
2156 //auto y2 = x.get!(immutable int)();
2157 }
2158
2159 // test iteration
2160 @system unittest
2161 {
2162 auto v = Variant([ 1, 2, 3, 4 ][]);
2163 auto j = 0;
foreach(int i;v)2164 foreach (int i; v)
2165 {
2166 assert(i == ++j);
2167 }
2168 assert(j == 4);
2169 }
2170
2171 // test convertibility
2172 @system unittest
2173 {
2174 auto v = Variant("abc".dup);
2175 assert(v.convertsTo!(char[]));
2176 }
2177
2178 // https://issues.dlang.org/show_bug.cgi?id=5424
2179 @system unittest
2180 {
2181 interface A {
2182 void func1();
2183 }
2184 static class AC: A {
func1()2185 void func1() {
2186 }
2187 }
2188
2189 A a = new AC();
2190 a.func1();
2191 Variant b = Variant(a);
2192 }
2193
2194 // https://issues.dlang.org/show_bug.cgi?id=7070
2195 @system unittest
2196 {
2197 Variant v;
2198 v = null;
2199 }
2200
2201 // Class and interface opEquals, https://issues.dlang.org/show_bug.cgi?id=12157
2202 @system unittest
2203 {
2204 class Foo { }
2205
2206 class DerivedFoo : Foo { }
2207
2208 Foo f1 = new Foo();
2209 Foo f2 = new DerivedFoo();
2210
2211 Variant v1 = f1, v2 = f2;
2212 assert(v1 == f1);
2213 assert(v1 != new Foo());
2214 assert(v1 != f2);
2215 assert(v2 != v1);
2216 assert(v2 == f2);
2217 }
2218
2219 // Const parameters with opCall, https://issues.dlang.org/show_bug.cgi?id=11361
2220 @system unittest
2221 {
t1(string c)2222 static string t1(string c) {
2223 return c ~ "a";
2224 }
2225
t2(const (char)[]p)2226 static const(char)[] t2(const(char)[] p) {
2227 return p ~ "b";
2228 }
2229
t3(int p)2230 static char[] t3(int p) {
2231 import std.conv : text;
2232 return p.text.dup;
2233 }
2234
2235 Variant v1 = &t1;
2236 Variant v2 = &t2;
2237 Variant v3 = &t3;
2238
2239 assert(v1("abc") == "abca");
2240 assert(v1("abc").type == typeid(string));
2241 assert(v2("abc") == "abcb");
2242
2243 assert(v2(cast(char[])("abc".dup)) == "abcb");
2244 assert(v2("abc").type == typeid(const(char)[]));
2245
2246 assert(v3(4) == ['4']);
2247 assert(v3(4).type == typeid(char[]));
2248 }
2249
2250 // https://issues.dlang.org/show_bug.cgi?id=12071
2251 @system unittest
2252 {
2253 static struct Structure { int data; }
2254 alias VariantTest = Algebraic!(Structure delegate() pure nothrow @nogc @safe);
2255
2256 bool called = false;
example()2257 Structure example() pure nothrow @nogc @safe
2258 {
2259 called = true;
2260 return Structure.init;
2261 }
2262 auto m = VariantTest(&example);
2263 m();
2264 assert(called);
2265 }
2266
2267 // Ordering comparisons of incompatible types
2268 // e.g. https://issues.dlang.org/show_bug.cgi?id=7990
2269 @system unittest
2270 {
2271 import std.exception : assertThrown;
2272 assertThrown!VariantException(Variant(3) < "a");
2273 assertThrown!VariantException("a" < Variant(3));
2274 assertThrown!VariantException(Variant(3) < Variant("a"));
2275
2276 assertThrown!VariantException(Variant.init < Variant(3));
2277 assertThrown!VariantException(Variant(3) < Variant.init);
2278 }
2279
2280 // Handling of unordered types
2281 // https://issues.dlang.org/show_bug.cgi?id=9043
2282 @system unittest
2283 {
2284 import std.exception : assertThrown;
2285 static struct A { int a; }
2286
2287 assert(Variant(A(3)) != A(4));
2288
2289 assertThrown!VariantException(Variant(A(3)) < A(4));
2290 assertThrown!VariantException(A(3) < Variant(A(4)));
2291 assertThrown!VariantException(Variant(A(3)) < Variant(A(4)));
2292 }
2293
2294 // Handling of empty types and arrays
2295 // https://issues.dlang.org/show_bug.cgi?id=10958
2296 @system unittest
2297 {
2298 class EmptyClass { }
2299 struct EmptyStruct { }
2300 alias EmptyArray = void[0];
2301 alias Alg = Algebraic!(EmptyClass, EmptyStruct, EmptyArray);
2302
testEmpty(T)2303 Variant testEmpty(T)()
2304 {
2305 T inst;
2306 Variant v = inst;
2307 assert(v.get!T == inst);
2308 assert(v.peek!T !is null);
2309 assert(*v.peek!T == inst);
2310 Alg alg = inst;
2311 assert(alg.get!T == inst);
2312 return v;
2313 }
2314
2315 testEmpty!EmptyClass();
2316 testEmpty!EmptyStruct();
2317 testEmpty!EmptyArray();
2318
2319 // EmptyClass/EmptyStruct sizeof is 1, so we have this to test just size 0.
2320 EmptyArray arr = EmptyArray.init;
2321 Algebraic!(EmptyArray) a = arr;
2322 assert(a.length == 0);
2323 assert(a.get!EmptyArray == arr);
2324 }
2325
2326 // Handling of void function pointers / delegates
2327 // https://issues.dlang.org/show_bug.cgi?id=11360
2328 @system unittest
2329 {
t1()2330 static void t1() { }
2331 Variant v = &t1;
2332 assert(v() == Variant.init);
2333
t2()2334 static int t2() { return 3; }
2335 Variant v2 = &t2;
2336 assert(v2() == 3);
2337 }
2338
2339 // Using peek for large structs
2340 // https://issues.dlang.org/show_bug.cgi?id=8580
2341 @system unittest
2342 {
TestStruct(bool pad)2343 struct TestStruct(bool pad)
2344 {
2345 int val1;
2346 static if (pad)
2347 ubyte[Variant.size] padding;
2348 int val2;
2349 }
2350
testPeekWith(T)2351 void testPeekWith(T)()
2352 {
2353 T inst;
2354 inst.val1 = 3;
2355 inst.val2 = 4;
2356 Variant v = inst;
2357 T* original = v.peek!T;
2358 assert(original.val1 == 3);
2359 assert(original.val2 == 4);
2360 original.val1 = 6;
2361 original.val2 = 8;
2362 T modified = v.get!T;
2363 assert(modified.val1 == 6);
2364 assert(modified.val2 == 8);
2365 }
2366
2367 testPeekWith!(TestStruct!false)();
2368 testPeekWith!(TestStruct!true)();
2369 }
2370
2371 // https://issues.dlang.org/show_bug.cgi?id=18780
2372 @system unittest
2373 {
2374 int x = 7;
2375 Variant a = x;
2376 assert(a.convertsTo!ulong);
2377 assert(a.convertsTo!uint);
2378 }
2379
2380 /**
2381 * Applies a delegate or function to the given $(LREF Algebraic) depending on the held type,
2382 * ensuring that all types are handled by the visiting functions.
2383 *
2384 * The delegate or function having the currently held value as parameter is called
2385 * with `variant`'s current value. Visiting handlers are passed
2386 * in the template parameter list.
2387 * It is statically ensured that all held types of
2388 * `variant` are handled across all handlers.
2389 * `visit` allows delegates and static functions to be passed
2390 * as parameters.
2391 *
2392 * If a function with an untyped parameter is specified, this function is called
2393 * when the variant contains a type that does not match any other function.
2394 * This can be used to apply the same function across multiple possible types.
2395 * Exactly one generic function is allowed.
2396 *
2397 * If a function without parameters is specified, this function is called
2398 * when `variant` doesn't hold a value. Exactly one parameter-less function
2399 * is allowed.
2400 *
2401 * Duplicate overloads matching the same type in one of the visitors are disallowed.
2402 *
2403 * Returns: The return type of visit is deduced from the visiting functions and must be
2404 * the same across all overloads.
2405 * Throws: $(LREF VariantException) if `variant` doesn't hold a value and no
2406 * parameter-less fallback function is specified.
2407 */
2408 template visit(Handlers...)
2409 if (Handlers.length > 0)
2410 {
2411 ///
2412 auto visit(VariantType)(VariantType variant)
2413 if (isAlgebraic!VariantType)
2414 {
2415 return visitImpl!(true, VariantType, Handlers)(variant);
2416 }
2417 }
2418
2419 ///
2420 @system unittest
2421 {
2422 Algebraic!(int, string) variant;
2423
2424 variant = 10;
2425 assert(variant.visit!((string s) => cast(int) s.length,
2426 (int i) => i)()
2427 == 10);
2428 variant = "string";
2429 assert(variant.visit!((int i) => i,
2430 (string s) => cast(int) s.length)()
2431 == 6);
2432
2433 // Error function usage
2434 Algebraic!(int, string) emptyVar;
2435 auto rslt = emptyVar.visit!((string s) => cast(int) s.length,
2436 (int i) => i,
2437 () => -1)();
2438 assert(rslt == -1);
2439
2440 // Generic function usage
2441 Algebraic!(int, float, real) number = 2;
2442 assert(number.visit!(x => x += 1) == 3);
2443
2444 // Generic function for int/float with separate behavior for string
2445 Algebraic!(int, float, string) something = 2;
2446 assert(something.visit!((string s) => s.length, x => x) == 2); // generic
2447 something = "asdf";
2448 assert(something.visit!((string s) => s.length, x => x) == 4); // string
2449
2450 // Generic handler and empty handler
2451 Algebraic!(int, float, real) empty2;
2452 assert(empty2.visit!(x => x + 1, () => -1) == -1);
2453 }
2454
2455 @system unittest
2456 {
2457 Algebraic!(size_t, string) variant;
2458
2459 // not all handled check
2460 static assert(!__traits(compiles, variant.visit!((size_t i){ })() ));
2461
2462 variant = cast(size_t) 10;
2463 auto which = 0;
2464 variant.visit!( (string s) => which = 1,
2465 (size_t i) => which = 0
2466 )();
2467
2468 // integer overload was called
2469 assert(which == 0);
2470
2471 // mustn't compile as generic Variant not supported
2472 Variant v;
2473 static assert(!__traits(compiles, v.visit!((string s) => which = 1,
2474 (size_t i) => which = 0
2475 )()
2476 ));
2477
func(string s)2478 static size_t func(string s) {
2479 return s.length;
2480 }
2481
2482 variant = "test";
2483 assert( 4 == variant.visit!(func,
2484 (size_t i) => i
2485 )());
2486
2487 Algebraic!(int, float, string) variant2 = 5.0f;
2488 // Shouldn' t compile as float not handled by visitor.
2489 static assert(!__traits(compiles, variant2.visit!(
2490 (int _) {},
2491 (string _) {})()));
2492
2493 Algebraic!(size_t, string, float) variant3;
2494 variant3 = 10.0f;
2495 auto floatVisited = false;
2496
2497 assert(variant3.visit!(
2498 (float f) { floatVisited = true; return cast(size_t) f; },
2499 func,
2500 (size_t i) { return i; }
2501 )() == 10);
2502 assert(floatVisited == true);
2503
2504 Algebraic!(float, string) variant4;
2505
2506 assert(variant4.visit!(func, (float f) => cast(size_t) f, () => size_t.max)() == size_t.max);
2507
2508 // double error func check
2509 static assert(!__traits(compiles,
2510 visit!(() => size_t.max, func, (float f) => cast(size_t) f, () => size_t.max)(variant4))
2511 );
2512 }
2513
2514 // disallow providing multiple generic handlers to visit
2515 // disallow a generic handler that does not apply to all types
2516 @system unittest
2517 {
2518 Algebraic!(int, float) number = 2;
2519 // ok, x + 1 valid for int and float
2520 static assert( __traits(compiles, number.visit!(x => x + 1)));
2521 // bad, two generic handlers
2522 static assert(!__traits(compiles, number.visit!(x => x + 1, x => x + 2)));
2523 // bad, x ~ "a" does not apply to int or float
2524 static assert(!__traits(compiles, number.visit!(x => x ~ "a")));
2525 // bad, x ~ "a" does not apply to int or float
2526 static assert(!__traits(compiles, number.visit!(x => x + 1, x => x ~ "a")));
2527
2528 Algebraic!(int, string) maybenumber = 2;
2529 // ok, x ~ "a" valid for string, x + 1 valid for int, only 1 generic
2530 static assert( __traits(compiles, maybenumber.visit!((string x) => x ~ "a", x => "foobar"[0 .. x + 1])));
2531 // bad, x ~ "a" valid for string but not int
2532 static assert(!__traits(compiles, maybenumber.visit!(x => x ~ "a")));
2533 // bad, two generics, each only applies in one case
2534 static assert(!__traits(compiles, maybenumber.visit!(x => x + 1, x => x ~ "a")));
2535 }
2536
2537 /**
2538 * Behaves as $(LREF visit) but doesn't enforce that all types are handled
2539 * by the visiting functions.
2540 *
2541 * If a parameter-less function is specified it is called when
2542 * either `variant` doesn't hold a value or holds a type
2543 * which isn't handled by the visiting functions.
2544 *
2545 * Returns: The return type of tryVisit is deduced from the visiting functions and must be
2546 * the same across all overloads.
2547 * Throws: $(LREF VariantException) if `variant` doesn't hold a value or
2548 * `variant` holds a value which isn't handled by the visiting functions,
2549 * when no parameter-less fallback function is specified.
2550 */
2551 template tryVisit(Handlers...)
2552 if (Handlers.length > 0)
2553 {
2554 ///
2555 auto tryVisit(VariantType)(VariantType variant)
2556 if (isAlgebraic!VariantType)
2557 {
2558 return visitImpl!(false, VariantType, Handlers)(variant);
2559 }
2560 }
2561
2562 ///
2563 @system unittest
2564 {
2565 Algebraic!(int, string) variant;
2566
2567 variant = 10;
2568 auto which = -1;
2569 variant.tryVisit!((int i) { which = 0; })();
2570 assert(which == 0);
2571
2572 // Error function usage
2573 variant = "test";
2574 variant.tryVisit!((int i) { which = 0; },
2575 () { which = -100; })();
2576 assert(which == -100);
2577 }
2578
2579 @system unittest
2580 {
2581 import std.exception : assertThrown;
2582 Algebraic!(int, string) variant;
2583
2584 variant = 10;
2585 auto which = -1;
2586 variant.tryVisit!((int i){ which = 0; })();
2587
2588 assert(which == 0);
2589
2590 variant = "test";
2591
2592 assertThrown!VariantException(variant.tryVisit!((int i) { which = 0; })());
2593
errorfunc()2594 void errorfunc()
2595 {
2596 which = -1;
2597 }
2598
2599 variant.tryVisit!((int i) { which = 0; }, errorfunc)();
2600
2601 assert(which == -1);
2602 }
2603
isAlgebraic(Type)2604 private template isAlgebraic(Type)
2605 {
2606 static if (is(Type _ == VariantN!T, T...))
2607 enum isAlgebraic = T.length >= 2; // T[0] == maxDataSize, T[1..$] == AllowedTypesParam
2608 else
2609 enum isAlgebraic = false;
2610 }
2611
2612 @system unittest
2613 {
2614 static assert(!isAlgebraic!(Variant));
2615 static assert( isAlgebraic!(Algebraic!(string)));
2616 static assert( isAlgebraic!(Algebraic!(int, int[])));
2617 }
2618
2619 private auto visitImpl(bool Strict, VariantType, Handler...)(VariantType variant)
2620 if (isAlgebraic!VariantType && Handler.length > 0)
2621 {
2622 alias AllowedTypes = VariantType.AllowedTypes;
2623
2624
2625 /**
2626 * Returns: Struct where `indices` is an array which
2627 * contains at the n-th position the index in Handler which takes the
2628 * n-th type of AllowedTypes. If an Handler doesn't match an
2629 * AllowedType, -1 is set. If a function in the delegates doesn't
2630 * have parameters, the field `exceptionFuncIdx` is set;
2631 * otherwise it's -1.
2632 */
visitGetOverloadMap()2633 auto visitGetOverloadMap()
2634 {
2635 struct Result {
2636 int[AllowedTypes.length] indices;
2637 int exceptionFuncIdx = -1;
2638 int generalFuncIdx = -1;
2639 }
2640
2641 Result result;
2642
2643 enum int nonmatch = ()
2644 {
2645 foreach (int dgidx, dg; Handler)
2646 {
2647 bool found = false;
2648 foreach (T; AllowedTypes)
2649 {
2650 found |= __traits(compiles, { static assert(isSomeFunction!(dg!T)); });
2651 found |= __traits(compiles, (T t) { dg(t); });
2652 found |= __traits(compiles, dg());
2653 }
2654 if (!found) return dgidx;
2655 }
2656 return -1;
2657 }();
2658 static assert(nonmatch == -1, "No match for visit handler #"~
2659 nonmatch.stringof~" ("~Handler[nonmatch].stringof~")");
2660
2661 foreach (tidx, T; AllowedTypes)
2662 {
2663 bool added = false;
2664 foreach (dgidx, dg; Handler)
2665 {
2666 // Handle normal function objects
2667 static if (isSomeFunction!dg)
2668 {
2669 alias Params = Parameters!dg;
2670 static if (Params.length == 0)
2671 {
2672 // Just check exception functions in the first
2673 // inner iteration (over delegates)
2674 if (tidx > 0)
2675 continue;
2676 else
2677 {
2678 if (result.exceptionFuncIdx != -1)
2679 assert(false, "duplicate parameter-less (error-)function specified");
2680 result.exceptionFuncIdx = dgidx;
2681 }
2682 }
2683 else static if (is(Params[0] == T) || is(Unqual!(Params[0]) == T))
2684 {
2685 if (added)
2686 assert(false, "duplicate overload specified for type '" ~ T.stringof ~ "'");
2687
2688 added = true;
2689 result.indices[tidx] = dgidx;
2690 }
2691 }
2692 else static if (__traits(compiles, { static assert(isSomeFunction!(dg!T)); }))
2693 {
2694 assert(result.generalFuncIdx == -1 ||
2695 result.generalFuncIdx == dgidx,
2696 "Only one generic visitor function is allowed");
2697 result.generalFuncIdx = dgidx;
2698 }
2699 // Handle composite visitors with opCall overloads
2700 }
2701
2702 if (!added)
2703 result.indices[tidx] = -1;
2704 }
2705
2706 return result;
2707 }
2708
2709 enum HandlerOverloadMap = visitGetOverloadMap();
2710
2711 if (!variant.hasValue)
2712 {
2713 // Call the exception function. The HandlerOverloadMap
2714 // will have its exceptionFuncIdx field set to value != -1 if an
2715 // exception function has been specified; otherwise we just through an exception.
2716 static if (HandlerOverloadMap.exceptionFuncIdx != -1)
2717 return Handler[ HandlerOverloadMap.exceptionFuncIdx ]();
2718 else
2719 throw new VariantException("variant must hold a value before being visited.");
2720 }
2721
foreach(idx,T;AllowedTypes)2722 foreach (idx, T; AllowedTypes)
2723 {
2724 if (auto ptr = variant.peek!T)
2725 {
2726 enum dgIdx = HandlerOverloadMap.indices[idx];
2727
2728 static if (dgIdx == -1)
2729 {
2730 static if (HandlerOverloadMap.generalFuncIdx >= 0)
2731 return Handler[HandlerOverloadMap.generalFuncIdx](*ptr);
2732 else static if (Strict)
2733 static assert(false, "overload for type '" ~ T.stringof ~ "' hasn't been specified");
2734 else static if (HandlerOverloadMap.exceptionFuncIdx != -1)
2735 return Handler[HandlerOverloadMap.exceptionFuncIdx]();
2736 else
2737 throw new VariantException(
2738 "variant holds value of type '"
2739 ~ T.stringof ~
2740 "' but no visitor has been provided"
2741 );
2742 }
2743 else
2744 {
2745 return Handler[ dgIdx ](*ptr);
2746 }
2747 }
2748 }
2749
2750 assert(false);
2751 }
2752
2753 // https://issues.dlang.org/show_bug.cgi?id=21253
2754 @system unittest
2755 {
2756 static struct A { int n; }
2757 static struct B { }
2758
2759 auto a = Algebraic!(A, B)(B());
2760 assert(a.visit!(
2761 (B _) => 42,
2762 (a ) => a.n
2763 ) == 42);
2764 }
2765
2766 @system unittest
2767 {
2768 // validate that visit can be called with a const type
2769 struct Foo { int depth; }
2770 struct Bar { int depth; }
2771 alias FooBar = Algebraic!(Foo, Bar);
2772
depth(in FooBar fb)2773 int depth(in FooBar fb) {
2774 return fb.visit!((Foo foo) => foo.depth,
2775 (Bar bar) => bar.depth);
2776 }
2777
2778 FooBar fb = Foo(3);
2779 assert(depth(fb) == 3);
2780 }
2781
2782 // https://issues.dlang.org/show_bug.cgi?id=16383
2783 @system unittest
2784 {
this()2785 class Foo {this() immutable {}}
2786 alias V = Algebraic!(immutable Foo);
2787
2788 auto x = V(new immutable Foo).visit!(
2789 (immutable(Foo) _) => 3
2790 );
2791 assert(x == 3);
2792 }
2793
2794 // https://issues.dlang.org/show_bug.cgi?id=5310
2795 @system unittest
2796 {
2797 const Variant a;
2798 assert(a == a);
2799 Variant b;
2800 assert(a == b);
2801 assert(b == a);
2802 }
2803
2804 @system unittest
2805 {
2806 const Variant a = [2];
2807 assert(a[0] == 2);
2808 }
2809
2810 // https://issues.dlang.org/show_bug.cgi?id=10017
2811 @system unittest
2812 {
2813 static struct S
2814 {
2815 ubyte[Variant.size + 1] s;
2816 }
2817
2818 Variant v1, v2;
2819 v1 = S(); // the payload is allocated on the heap
2820 v2 = v1; // AssertError: target must be non-null
2821 assert(v1 == v2);
2822 }
2823
2824 // https://issues.dlang.org/show_bug.cgi?id=7069
2825 @system unittest
2826 {
2827 import std.exception : assertThrown;
2828 Variant v;
2829
2830 int i = 10;
2831 v = i;
2832 static foreach (qual; AliasSeq!(Alias, ConstOf))
2833 {
2834 assert(v.get!(qual!int) == 10);
2835 assert(v.get!(qual!float) == 10.0f);
2836 }
2837 static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf))
2838 {
2839 assertThrown!VariantException(v.get!(qual!int));
2840 }
2841
2842 const(int) ci = 20;
2843 v = ci;
2844 static foreach (qual; AliasSeq!(ConstOf))
2845 {
2846 assert(v.get!(qual!int) == 20);
2847 assert(v.get!(qual!float) == 20.0f);
2848 }
2849 static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf))
2850 {
2851 assertThrown!VariantException(v.get!(qual!int));
2852 assertThrown!VariantException(v.get!(qual!float));
2853 }
2854
2855 immutable(int) ii = ci;
2856 v = ii;
2857 static foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf))
2858 {
2859 assert(v.get!(qual!int) == 20);
2860 assert(v.get!(qual!float) == 20.0f);
2861 }
2862 static foreach (qual; AliasSeq!(Alias, SharedOf))
2863 {
2864 assertThrown!VariantException(v.get!(qual!int));
2865 assertThrown!VariantException(v.get!(qual!float));
2866 }
2867
2868 int[] ai = [1,2,3];
2869 v = ai;
2870 static foreach (qual; AliasSeq!(Alias, ConstOf))
2871 {
2872 assert(v.get!(qual!(int[])) == [1,2,3]);
2873 assert(v.get!(qual!(int)[]) == [1,2,3]);
2874 }
2875 static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf))
2876 {
2877 assertThrown!VariantException(v.get!(qual!(int[])));
2878 assertThrown!VariantException(v.get!(qual!(int)[]));
2879 }
2880
2881 const(int[]) cai = [4,5,6];
2882 v = cai;
2883 static foreach (qual; AliasSeq!(ConstOf))
2884 {
2885 assert(v.get!(qual!(int[])) == [4,5,6]);
2886 assert(v.get!(qual!(int)[]) == [4,5,6]);
2887 }
2888 static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf))
2889 {
2890 assertThrown!VariantException(v.get!(qual!(int[])));
2891 assertThrown!VariantException(v.get!(qual!(int)[]));
2892 }
2893
2894 immutable(int[]) iai = [7,8,9];
2895 v = iai;
2896 //assert(v.get!(immutable(int[])) == [7,8,9]); // Bug ??? runtime error
2897 assert(v.get!(immutable(int)[]) == [7,8,9]);
2898 assert(v.get!(const(int[])) == [7,8,9]);
2899 assert(v.get!(const(int)[]) == [7,8,9]);
2900 //assert(v.get!(shared(const(int[]))) == cast(shared const)[7,8,9]); // Bug ??? runtime error
2901 //assert(v.get!(shared(const(int))[]) == cast(shared const)[7,8,9]); // Bug ??? runtime error
2902 static foreach (qual; AliasSeq!(Alias))
2903 {
2904 assertThrown!VariantException(v.get!(qual!(int[])));
2905 assertThrown!VariantException(v.get!(qual!(int)[]));
2906 }
2907
2908 class A {}
2909 class B : A {}
2910 B b = new B();
2911 v = b;
2912 static foreach (qual; AliasSeq!(Alias, ConstOf))
2913 {
2914 assert(v.get!(qual!B) is b);
2915 assert(v.get!(qual!A) is b);
2916 assert(v.get!(qual!Object) is b);
2917 }
2918 static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf))
2919 {
2920 assertThrown!VariantException(v.get!(qual!B));
2921 assertThrown!VariantException(v.get!(qual!A));
2922 assertThrown!VariantException(v.get!(qual!Object));
2923 }
2924
2925 const(B) cb = new B();
2926 v = cb;
2927 static foreach (qual; AliasSeq!(ConstOf))
2928 {
2929 assert(v.get!(qual!B) is cb);
2930 assert(v.get!(qual!A) is cb);
2931 assert(v.get!(qual!Object) is cb);
2932 }
2933 static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf))
2934 {
2935 assertThrown!VariantException(v.get!(qual!B));
2936 assertThrown!VariantException(v.get!(qual!A));
2937 assertThrown!VariantException(v.get!(qual!Object));
2938 }
2939
2940 immutable(B) ib = new immutable(B)();
2941 v = ib;
2942 static foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf))
2943 {
2944 assert(v.get!(qual!B) is ib);
2945 assert(v.get!(qual!A) is ib);
2946 assert(v.get!(qual!Object) is ib);
2947 }
2948 static foreach (qual; AliasSeq!(Alias, SharedOf))
2949 {
2950 assertThrown!VariantException(v.get!(qual!B));
2951 assertThrown!VariantException(v.get!(qual!A));
2952 assertThrown!VariantException(v.get!(qual!Object));
2953 }
2954
2955 shared(B) sb = new shared B();
2956 v = sb;
2957 static foreach (qual; AliasSeq!(SharedOf, SharedConstOf))
2958 {
2959 assert(v.get!(qual!B) is sb);
2960 assert(v.get!(qual!A) is sb);
2961 assert(v.get!(qual!Object) is sb);
2962 }
2963 static foreach (qual; AliasSeq!(Alias, ImmutableOf, ConstOf))
2964 {
2965 assertThrown!VariantException(v.get!(qual!B));
2966 assertThrown!VariantException(v.get!(qual!A));
2967 assertThrown!VariantException(v.get!(qual!Object));
2968 }
2969
2970 shared(const(B)) scb = new shared const B();
2971 v = scb;
2972 static foreach (qual; AliasSeq!(SharedConstOf))
2973 {
2974 assert(v.get!(qual!B) is scb);
2975 assert(v.get!(qual!A) is scb);
2976 assert(v.get!(qual!Object) is scb);
2977 }
2978 static foreach (qual; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf))
2979 {
2980 assertThrown!VariantException(v.get!(qual!B));
2981 assertThrown!VariantException(v.get!(qual!A));
2982 assertThrown!VariantException(v.get!(qual!Object));
2983 }
2984 }
2985
2986 // https://issues.dlang.org/show_bug.cgi?id=12540
2987 @system unittest
2988 {
2989 static struct DummyScope
2990 {
2991 alias Alias12540 = Algebraic!Class12540;
2992
2993 static class Class12540
2994 {
2995 Alias12540 entity;
2996 }
2997 }
2998 }
2999
3000 @system unittest
3001 {
3002 // https://issues.dlang.org/show_bug.cgi?id=10194
3003 // Also test for elaborate copying
3004 static struct S
3005 {
3006 @disable this();
3007 this(int dummy)
3008 {
3009 ++cnt;
3010 }
3011
3012 this(this)
3013 {
3014 ++cnt;
3015 }
3016
3017 @disable S opAssign();
3018
3019 ~this()
3020 {
3021 --cnt;
3022 assert(cnt >= 0);
3023 }
3024 static int cnt = 0;
3025 }
3026
3027 {
3028 Variant v;
3029 {
3030 v = S(0);
3031 assert(S.cnt == 1);
3032 }
3033 assert(S.cnt == 1);
3034
3035 // assigning a new value should destroy the existing one
3036 v = 0;
3037 assert(S.cnt == 0);
3038
3039 // destroying the variant should destroy it's current value
3040 v = S(0);
3041 assert(S.cnt == 1);
3042 }
3043 assert(S.cnt == 0);
3044 }
3045
3046 @system unittest
3047 {
3048 // https://issues.dlang.org/show_bug.cgi?id=13300
3049 static struct S
3050 {
3051 this(this) {}
3052 ~this() {}
3053 }
3054
3055 static assert( hasElaborateCopyConstructor!(Variant));
3056 static assert(!hasElaborateCopyConstructor!(Algebraic!bool));
3057 static assert( hasElaborateCopyConstructor!(Algebraic!S));
3058 static assert( hasElaborateCopyConstructor!(Algebraic!(bool, S)));
3059
3060 static assert( hasElaborateDestructor!(Variant));
3061 static assert(!hasElaborateDestructor!(Algebraic!bool));
3062 static assert( hasElaborateDestructor!(Algebraic!S));
3063 static assert( hasElaborateDestructor!(Algebraic!(bool, S)));
3064
3065 import std.array;
3066 alias Value = Algebraic!bool;
3067
3068 static struct T
3069 {
3070 Value value;
3071 @disable this();
3072 }
3073 auto a = appender!(T[]);
3074 }
3075
3076 // https://issues.dlang.org/show_bug.cgi?id=13871
3077 @system unittest
3078 {
3079 alias A = Algebraic!(int, typeof(null));
3080 static struct B { A value; }
3081 alias C = std.variant.Algebraic!B;
3082
3083 C var;
3084 var = C(B());
3085 }
3086
3087 @system unittest
3088 {
3089 import std.exception : assertThrown, assertNotThrown;
3090 // Make sure Variant can handle types with opDispatch but no length field.
3091 struct SWithNoLength
3092 {
3093 void opDispatch(string s)() { }
3094 }
3095
3096 struct SWithLength
3097 {
3098 @property int opDispatch(string s)()
3099 {
3100 // Assume that s == "length"
3101 return 5; // Any value is OK for test.
3102 }
3103 }
3104
3105 SWithNoLength sWithNoLength;
3106 Variant v = sWithNoLength;
3107 assertThrown!VariantException(v.length);
3108
3109 SWithLength sWithLength;
3110 v = sWithLength;
3111 assertNotThrown!VariantException(v.get!SWithLength.length);
3112 assertThrown!VariantException(v.length);
3113 }
3114
3115 // https://issues.dlang.org/show_bug.cgi?id=13534
3116 @system unittest
3117 {
3118 static assert(!__traits(compiles, () @safe {
3119 auto foo() @system { return 3; }
3120 auto v = Variant(&foo);
3121 v(); // foo is called in safe code!?
3122 }));
3123 }
3124
3125 // https://issues.dlang.org/show_bug.cgi?id=15039
3126 @system unittest
3127 {
3128 import std.typecons;
3129 import std.variant;
3130
3131 alias IntTypedef = Typedef!int;
3132 alias Obj = Algebraic!(int, IntTypedef, This[]);
3133
3134 Obj obj = 1;
3135
3136 obj.visit!(
3137 (int x) {},
3138 (IntTypedef x) {},
3139 (Obj[] x) {},
3140 );
3141 }
3142
3143 // https://issues.dlang.org/show_bug.cgi?id=15791
3144 @system unittest
3145 {
3146 int n = 3;
3147 struct NS1 { int foo() { return n + 10; } }
3148 struct NS2 { int foo() { return n * 10; } }
3149
3150 Variant v;
3151 v = NS1();
3152 assert(v.get!NS1.foo() == 13);
3153 v = NS2();
3154 assert(v.get!NS2.foo() == 30);
3155 }
3156
3157 // https://issues.dlang.org/show_bug.cgi?id=15827
3158 @system unittest
3159 {
3160 static struct Foo15827 { Variant v; this(Foo15827 v) {} }
3161 Variant v = Foo15827.init;
3162 }
3163
3164 // https://issues.dlang.org/show_bug.cgi?id=18934
3165 @system unittest
3166 {
3167 static struct S
3168 {
3169 const int x;
3170 }
3171
3172 auto s = S(42);
3173 Variant v = s;
3174 auto s2 = v.get!S;
3175 assert(s2.x == 42);
3176 Variant v2 = v; // support copying from one variant to the other
3177 v2 = S(2);
3178 v = v2;
3179 assert(v.get!S.x == 2);
3180 }
3181
3182 // https://issues.dlang.org/show_bug.cgi?id=19200
3183 @system unittest
3184 {
3185 static struct S
3186 {
3187 static int opBinaryRight(string op : "|", T)(T rhs)
3188 {
3189 return 3;
3190 }
3191 }
3192
3193 S s;
3194 Variant v;
3195 auto b = v | s;
3196 assert(b == 3);
3197 }
3198
3199 // https://issues.dlang.org/show_bug.cgi?id=11061
3200 @system unittest
3201 {
3202 int[4] el = [0, 1, 2, 3];
3203 int[3] nl = [0, 1, 2];
3204 Variant v1 = el;
3205 assert(v1 == el); // Compare Var(static) to static
3206 assert(v1 != nl); // Compare static arrays of different length
3207 assert(v1 == [0, 1, 2, 3]); // Compare Var(static) to dynamic.
3208 assert(v1 != [0, 1, 2]);
3209 int[] dyn = [0, 1, 2, 3];
3210 v1 = dyn;
3211 assert(v1 == el); // Compare Var(dynamic) to static.
3212 assert(v1 == [0, 1] ~ [2, 3]); // Compare Var(dynamic) to dynamic
3213 }
3214
3215 // https://issues.dlang.org/show_bug.cgi?id=15940
3216 @system unittest
3217 {
3218 class C { }
3219 struct S
3220 {
3221 C a;
3222 alias a this;
3223 }
3224 S s = S(new C());
3225 auto v = Variant(s); // compile error
3226 }
3227
3228 @system unittest
3229 {
3230 // Test if we don't have scoping issues.
3231 Variant createVariant(int[] input)
3232 {
3233 int[2] el = [input[0], input[1]];
3234 Variant v = el;
3235 return v;
3236 }
3237 Variant v = createVariant([0, 1]);
3238 createVariant([2, 3]);
3239 assert(v == [0,1]);
3240 }
3241
3242 // https://issues.dlang.org/show_bug.cgi?id=19994
3243 @safe unittest
3244 {
3245 alias Inner = Algebraic!(This*);
3246 alias Outer = Algebraic!(Inner, This*);
3247
3248 static assert(is(Outer.AllowedTypes == AliasSeq!(Inner, Outer*)));
3249 }
3250
3251 // https://issues.dlang.org/show_bug.cgi?id=21296
3252 @system unittest
3253 {
3254 immutable aa = ["0": 0];
3255 auto v = Variant(aa); // compile error
3256 }
3257