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