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 +/ 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 */ 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() cast(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) 1195 if (is(Delegate == delegate)) 1196 { 1197 alias A = Parameters!(Delegate)[0]; 1198 if (type == typeid(A[])) 1199 { 1200 auto arr = get!(A[]); 1201 foreach (ref e; arr) 1202 { 1203 if (dg(e)) return 1; 1204 } 1205 } 1206 else static if (is(A == VariantN)) 1207 { 1208 foreach (i; 0 .. length) 1209 { 1210 // @@@TODO@@@: find a better way to not confuse 1211 // clients who think they change values stored in the 1212 // Variant when in fact they are only changing tmp. 1213 auto tmp = this[i]; 1214 debug scope(exit) assert(tmp == this[i]); 1215 if (dg(tmp)) return 1; 1216 } 1217 } 1218 else 1219 { 1220 import std.conv : text; 1221 import std.exception : enforce; 1222 enforce(false, text("Variant type ", type, 1223 " not iterable with values of type ", 1224 A.stringof)); 1225 } 1226 return 0; 1227 } 1228 } 1229 1230 /// 1231 @system unittest 1232 { 1233 alias Var = VariantN!(maxSize!(int, double, string)); 1234 1235 Var a; // Must assign before use, otherwise exception ensues 1236 // Initialize with an integer; make the type int 1237 Var b = 42; 1238 assert(b.type == typeid(int)); 1239 // Peek at the value 1240 assert(b.peek!(int) !is null && *b.peek!(int) == 42); 1241 // Automatically convert per language rules 1242 auto x = b.get!(real); 1243 1244 // Assign any other type, including other variants 1245 a = b; 1246 a = 3.14; 1247 assert(a.type == typeid(double)); 1248 // Implicit conversions work just as with built-in types 1249 assert(a < b); 1250 // Check for convertibility 1251 assert(!a.convertsTo!(int)); // double not convertible to int 1252 // Strings and all other arrays are supported 1253 a = "now I'm a string"; 1254 assert(a == "now I'm a string"); 1255 } 1256 1257 /// can also assign arrays 1258 @system unittest 1259 { 1260 alias Var = VariantN!(maxSize!(int[])); 1261 1262 Var a = new int[42]; 1263 assert(a.length == 42); 1264 a[5] = 7; 1265 assert(a[5] == 7); 1266 } 1267 1268 @safe unittest 1269 { 1270 alias V = VariantN!24; 1271 const alignMask = V.alignof - 1; 1272 assert(V.sizeof == ((24 + (void*).sizeof + alignMask) & ~alignMask)); 1273 } 1274 1275 /// Can also assign class values 1276 @system unittest 1277 { 1278 alias Var = VariantN!(maxSize!(int*)); // classes are pointers 1279 Var a; 1280 1281 class Foo {} 1282 auto foo = new Foo; 1283 a = foo; 1284 assert(*a.peek!(Foo) == foo); // and full type information is preserved 1285 } 1286 1287 @system unittest 1288 { 1289 import std.conv : to; 1290 Variant v; 1291 int foo() { return 42; } 1292 v = &foo; 1293 assert(v() == 42); 1294 1295 static int bar(string s) { return to!int(s); } 1296 v = &bar; 1297 assert(v("43") == 43); 1298 } 1299 1300 @system unittest 1301 { 1302 int[int] hash = [ 42:24 ]; 1303 Variant v = hash; 1304 assert(v[42] == 24); 1305 v[42] = 5; 1306 assert(v[42] == 5); 1307 } 1308 1309 // opIndex with static arrays, https://issues.dlang.org/show_bug.cgi?id=12771 1310 @system unittest 1311 { 1312 int[4] elements = [0, 1, 2, 3]; 1313 Variant v = elements; 1314 assert(v == elements); 1315 assert(v[2] == 2); 1316 assert(v[3] == 3); 1317 v[2] = 6; 1318 assert(v[2] == 6); 1319 assert(v != elements); 1320 } 1321 1322 @system unittest 1323 { 1324 import std.exception : assertThrown; 1325 Algebraic!(int[]) v = [2, 2]; 1326 1327 assert(v == [2, 2]); 1328 v[0] = 1; 1329 assert(v[0] == 1); 1330 assert(v != [2, 2]); 1331 1332 // opIndexAssign from Variant 1333 v[1] = v[0]; 1334 assert(v[1] == 1); 1335 1336 static assert(!__traits(compiles, (v[1] = null))); 1337 assertThrown!VariantException(v[1] = Variant(null)); 1338 } 1339 1340 // https://issues.dlang.org/show_bug.cgi?id=10879 1341 @system unittest 1342 { 1343 int[10] arr = [1,2,3,4,5,6,7,8,9,10]; 1344 Variant v1 = arr; 1345 Variant v2; 1346 v2 = arr; 1347 assert(v1 == arr); 1348 assert(v2 == arr); 1349 foreach (i, e; arr) 1350 { 1351 assert(v1[i] == e); 1352 assert(v2[i] == e); 1353 } 1354 static struct LargeStruct 1355 { 1356 int[100] data; 1357 } 1358 LargeStruct ls; 1359 ls.data[] = 4; 1360 v1 = ls; 1361 Variant v3 = ls; 1362 assert(v1 == ls); 1363 assert(v3 == ls); 1364 } 1365 1366 // https://issues.dlang.org/show_bug.cgi?id=8195 1367 @system unittest 1368 { 1369 struct S 1370 { 1371 int a; 1372 long b; 1373 string c; 1374 real d = 0.0; 1375 bool e; 1376 } 1377 1378 static assert(S.sizeof >= Variant.sizeof); 1379 alias Types = AliasSeq!(string, int, S); 1380 alias MyVariant = VariantN!(maxSize!Types, Types); 1381 1382 auto v = MyVariant(S.init); 1383 assert(v == S.init); 1384 } 1385 1386 // https://issues.dlang.org/show_bug.cgi?id=10961 1387 @system unittest 1388 { 1389 // Primarily test that we can assign a void[] to a Variant. 1390 void[] elements = cast(void[])[1, 2, 3]; 1391 Variant v = elements; 1392 void[] returned = v.get!(void[]); 1393 assert(returned == elements); 1394 } 1395 1396 // https://issues.dlang.org/show_bug.cgi?id=13352 1397 @system unittest 1398 { 1399 alias TP = Algebraic!(long); 1400 auto a = TP(1L); 1401 auto b = TP(2L); 1402 assert(!TP.allowed!ulong); 1403 assert(a + b == 3L); 1404 assert(a + 2 == 3L); 1405 assert(1 + b == 3L); 1406 1407 alias TP2 = Algebraic!(long, string); 1408 auto c = TP2(3L); 1409 assert(a + c == 4L); 1410 } 1411 1412 // https://issues.dlang.org/show_bug.cgi?id=13354 1413 @system unittest 1414 { 1415 alias A = Algebraic!(string[]); 1416 A a = ["a", "b"]; 1417 assert(a[0] == "a"); 1418 assert(a[1] == "b"); 1419 a[1] = "c"; 1420 assert(a[1] == "c"); 1421 1422 alias AA = Algebraic!(int[string]); 1423 AA aa = ["a": 1, "b": 2]; 1424 assert(aa["a"] == 1); 1425 assert(aa["b"] == 2); 1426 aa["b"] = 3; 1427 assert(aa["b"] == 3); 1428 } 1429 1430 // https://issues.dlang.org/show_bug.cgi?id=14198 1431 @system unittest 1432 { 1433 Variant a = true; 1434 assert(a.type == typeid(bool)); 1435 } 1436 1437 // https://issues.dlang.org/show_bug.cgi?id=14233 1438 @system unittest 1439 { 1440 alias Atom = Algebraic!(string, This[]); 1441 1442 Atom[] values = []; 1443 auto a = Atom(values); 1444 } 1445 1446 pure nothrow @nogc 1447 @system unittest 1448 { 1449 Algebraic!(int, double) a; 1450 a = 100; 1451 a = 1.0; 1452 } 1453 1454 // https://issues.dlang.org/show_bug.cgi?id=14457 1455 @system unittest 1456 { 1457 alias A = Algebraic!(int, float, double); 1458 alias B = Algebraic!(int, float); 1459 1460 A a = 1; 1461 B b = 6f; 1462 a = b; 1463 1464 assert(a.type == typeid(float)); 1465 assert(a.get!float == 6f); 1466 } 1467 1468 // https://issues.dlang.org/show_bug.cgi?id=14585 1469 @system unittest 1470 { 1471 static struct S 1472 { 1473 int x = 42; 1474 ~this() {assert(x == 42);} 1475 } 1476 Variant(S()).get!S; 1477 } 1478 1479 // https://issues.dlang.org/show_bug.cgi?id=14586 1480 @system unittest 1481 { 1482 const Variant v = new immutable Object; 1483 v.get!(immutable Object); 1484 } 1485 1486 @system unittest 1487 { 1488 static struct S 1489 { 1490 T opCast(T)() {assert(false);} 1491 } 1492 Variant v = S(); 1493 v.get!S; 1494 } 1495 1496 // https://issues.dlang.org/show_bug.cgi?id=13262 1497 @system unittest 1498 { 1499 static void fun(T)(Variant v){ 1500 T x; 1501 v = x; 1502 auto r = v.get!(T); 1503 } 1504 Variant v; 1505 fun!(shared(int))(v); 1506 fun!(shared(int)[])(v); 1507 1508 static struct S1 1509 { 1510 int c; 1511 string a; 1512 } 1513 1514 static struct S2 1515 { 1516 string a; 1517 shared int[] b; 1518 } 1519 1520 static struct S3 1521 { 1522 string a; 1523 shared int[] b; 1524 int c; 1525 } 1526 1527 fun!(S1)(v); 1528 fun!(shared(S1))(v); 1529 fun!(S2)(v); 1530 fun!(shared(S2))(v); 1531 fun!(S3)(v); 1532 fun!(shared(S3))(v); 1533 1534 // ensure structs that are shared, but don't have shared postblits 1535 // can't be used. 1536 static struct S4 1537 { 1538 int x; 1539 this(this) {x = 0;} 1540 } 1541 1542 fun!(S4)(v); 1543 static assert(!is(typeof(fun!(shared(S4))(v)))); 1544 } 1545 1546 @safe unittest 1547 { 1548 Algebraic!(int) x; 1549 1550 static struct SafeS 1551 { 1552 @safe ~this() {} 1553 } 1554 1555 Algebraic!(SafeS) y; 1556 } 1557 1558 // https://issues.dlang.org/show_bug.cgi?id=19986 1559 @system unittest 1560 { 1561 VariantN!32 v; 1562 v = const(ubyte[33]).init; 1563 1564 struct S 1565 { 1566 ubyte[33] s; 1567 } 1568 1569 VariantN!32 v2; 1570 v2 = const(S).init; 1571 } 1572 1573 // https://issues.dlang.org/show_bug.cgi?id=21021 1574 @system unittest 1575 { 1576 static struct S 1577 { 1578 int h; 1579 int[5] array; 1580 alias h this; 1581 } 1582 1583 S msg; 1584 msg.array[] = 3; 1585 Variant a = msg; 1586 auto other = a.get!S; 1587 assert(msg.array[0] == 3); 1588 assert(other.array[0] == 3); 1589 } 1590 1591 // https://issues.dlang.org/show_bug.cgi?id=21231 1592 // Compatibility with -preview=fieldwise 1593 @system unittest 1594 { 1595 static struct Empty 1596 { 1597 bool opCmp(const scope ref Empty) const 1598 { return false; } 1599 } 1600 1601 Empty a, b; 1602 assert(a == b); 1603 assert(!(a < b)); 1604 1605 VariantN!(4, Empty) v = a; 1606 assert(v == b); 1607 assert(!(v < b)); 1608 } 1609 1610 // Compatibility with -preview=fieldwise 1611 @system unittest 1612 { 1613 static struct Empty 1614 { 1615 bool opEquals(const scope ref Empty) const 1616 { return false; } 1617 } 1618 1619 Empty a, b; 1620 assert(a != b); 1621 1622 VariantN!(4, Empty) v = a; 1623 assert(v != b); 1624 } 1625 1626 // https://issues.dlang.org/show_bug.cgi?id=22647 1627 // Can compare with 'null' 1628 @system unittest 1629 { 1630 static struct Bar 1631 { 1632 int* ptr; 1633 alias ptr this; 1634 } 1635 1636 static class Foo {} 1637 int* iptr; 1638 int[] arr; 1639 1640 Variant v = Foo.init; // 'null' 1641 assert(v != null); // can only compare objects with 'null' by using 'is' 1642 1643 v = iptr; 1644 assert(v == null); // pointers can be compared with 'null' 1645 1646 v = arr; 1647 assert(v == null); // arrays can be compared with 'null' 1648 1649 v = ""; 1650 assert(v == null); // strings are arrays, an empty string is considered 'null' 1651 1652 v = Bar.init; 1653 assert(v == null); // works with alias this 1654 1655 v = [3]; 1656 assert(v != null); 1657 assert(v > null); 1658 assert(v >= null); 1659 assert(!(v < null)); 1660 } 1661 1662 /** 1663 _Algebraic data type restricted to a closed set of possible 1664 types. It's an alias for $(LREF VariantN) with an 1665 appropriately-constructed maximum size. `Algebraic` is 1666 useful when it is desirable to restrict what a discriminated type 1667 could hold to the end of defining simpler and more efficient 1668 manipulation. 1669 1670 $(RED Warning: $(LREF Algebraic) is outdated and not recommended for use in new 1671 code. Instead, use $(REF SumType, std,sumtype).) 1672 */ 1673 template Algebraic(T...) 1674 { 1675 alias Algebraic = VariantN!(maxSize!T, T); 1676 } 1677 1678 /// 1679 @system unittest 1680 { 1681 auto v = Algebraic!(int, double, string)(5); 1682 assert(v.peek!(int)); 1683 v = 3.14; 1684 assert(v.peek!(double)); 1685 // auto x = v.peek!(long); // won't compile, type long not allowed 1686 // v = '1'; // won't compile, type char not allowed 1687 } 1688 1689 /** 1690 $(H4 Self-Referential Types) 1691 1692 A useful and popular use of algebraic data structures is for defining $(LUCKY 1693 self-referential data structures), i.e. structures that embed references to 1694 values of their own type within. 1695 1696 This is achieved with `Algebraic` by using `This` as a placeholder whenever a 1697 reference to the type being defined is needed. The `Algebraic` instantiation 1698 will perform $(LINK2 https://en.wikipedia.org/wiki/Name_resolution_(programming_languages)#Alpha_renaming_to_make_name_resolution_trivial, 1699 alpha renaming) on its constituent types, replacing `This` 1700 with the self-referenced type. The structure of the type involving `This` may 1701 be arbitrarily complex. 1702 */ 1703 @system unittest 1704 { 1705 import std.typecons : Tuple, tuple; 1706 1707 // A tree is either a leaf or a branch of two other trees 1708 alias Tree(Leaf) = Algebraic!(Leaf, Tuple!(This*, This*)); 1709 Tree!int tree = tuple(new Tree!int(42), new Tree!int(43)); 1710 Tree!int* right = tree.get!1[1]; 1711 assert(*right == 43); 1712 1713 // An object is a double, a string, or a hash of objects 1714 alias Obj = Algebraic!(double, string, This[string]); 1715 Obj obj = "hello"; 1716 assert(obj.get!1 == "hello"); 1717 obj = 42.0; 1718 assert(obj.get!0 == 42); 1719 obj = ["customer": Obj("John"), "paid": Obj(23.95)]; 1720 assert(obj.get!2["customer"] == "John"); 1721 } 1722 1723 private struct FakeComplexReal 1724 { 1725 real re, im; 1726 } 1727 1728 /** 1729 Alias for $(LREF VariantN) instantiated with the largest size of `creal`, 1730 `char[]`, and `void delegate()`. This ensures that `Variant` is large enough 1731 to hold all of D's predefined types unboxed, including all numeric types, 1732 pointers, delegates, and class references. You may want to use 1733 `VariantN` directly with a different maximum size either for 1734 storing larger types unboxed, or for saving memory. 1735 */ 1736 alias Variant = VariantN!(maxSize!(FakeComplexReal, char[], void delegate())); 1737 1738 /// 1739 @system unittest 1740 { 1741 Variant a; // Must assign before use, otherwise exception ensues 1742 // Initialize with an integer; make the type int 1743 Variant b = 42; 1744 assert(b.type == typeid(int)); 1745 // Peek at the value 1746 assert(b.peek!(int) !is null && *b.peek!(int) == 42); 1747 // Automatically convert per language rules 1748 auto x = b.get!(real); 1749 1750 // Assign any other type, including other variants 1751 a = b; 1752 a = 3.14; 1753 assert(a.type == typeid(double)); 1754 // Implicit conversions work just as with built-in types 1755 assert(a < b); 1756 // Check for convertibility 1757 assert(!a.convertsTo!(int)); // double not convertible to int 1758 // Strings and all other arrays are supported 1759 a = "now I'm a string"; 1760 assert(a == "now I'm a string"); 1761 } 1762 1763 /// can also assign arrays 1764 @system unittest 1765 { 1766 Variant a = new int[42]; 1767 assert(a.length == 42); 1768 a[5] = 7; 1769 assert(a[5] == 7); 1770 } 1771 1772 /// Can also assign class values 1773 @system unittest 1774 { 1775 Variant a; 1776 1777 class Foo {} 1778 auto foo = new Foo; 1779 a = foo; 1780 assert(*a.peek!(Foo) == foo); // and full type information is preserved 1781 } 1782 1783 /** 1784 * Returns an array of variants constructed from `args`. 1785 * 1786 * This is by design. During construction the `Variant` needs 1787 * static type information about the type being held, so as to store a 1788 * pointer to function for fast retrieval. 1789 */ 1790 Variant[] variantArray(T...)(T args) 1791 { 1792 Variant[] result; 1793 foreach (arg; args) 1794 { 1795 result ~= Variant(arg); 1796 } 1797 return result; 1798 } 1799 1800 /// 1801 @system unittest 1802 { 1803 auto a = variantArray(1, 3.14, "Hi!"); 1804 assert(a[1] == 3.14); 1805 auto b = Variant(a); // variant array as variant 1806 assert(b[1] == 3.14); 1807 } 1808 1809 /** 1810 * Thrown in three cases: 1811 * 1812 * $(OL $(LI An uninitialized `Variant` is used in any way except 1813 * assignment and `hasValue`;) $(LI A `get` or 1814 * `coerce` is attempted with an incompatible target type;) 1815 * $(LI A comparison between `Variant` objects of 1816 * incompatible types is attempted.)) 1817 * 1818 */ 1819 1820 // @@@ BUG IN COMPILER. THE 'STATIC' BELOW SHOULD NOT COMPILE 1821 static class VariantException : Exception 1822 { 1823 /// The source type in the conversion or comparison 1824 TypeInfo source; 1825 /// The target type in the conversion or comparison 1826 TypeInfo target; 1827 this(string s) 1828 { 1829 super(s); 1830 } 1831 this(TypeInfo source, TypeInfo target) 1832 { 1833 super("Variant: attempting to use incompatible types " 1834 ~ source.toString() 1835 ~ " and " ~ target.toString()); 1836 this.source = source; 1837 this.target = target; 1838 } 1839 } 1840 1841 /// 1842 @system unittest 1843 { 1844 import std.exception : assertThrown; 1845 1846 Variant v; 1847 1848 // uninitialized use 1849 assertThrown!VariantException(v + 1); 1850 assertThrown!VariantException(v.length); 1851 1852 // .get with an incompatible target type 1853 assertThrown!VariantException(Variant("a").get!int); 1854 1855 // comparison between incompatible types 1856 assertThrown!VariantException(Variant(3) < Variant("a")); 1857 } 1858 1859 @system unittest 1860 { 1861 alias W1 = This2Variant!(char, int, This[int]); 1862 alias W2 = AliasSeq!(int, char[int]); 1863 static assert(is(W1 == W2)); 1864 1865 alias var_t = Algebraic!(void, string); 1866 var_t foo = "quux"; 1867 } 1868 1869 @system unittest 1870 { 1871 alias A = Algebraic!(real, This[], This[int], This[This]); 1872 A v1, v2, v3; 1873 v2 = 5.0L; 1874 v3 = 42.0L; 1875 //v1 = [ v2 ][]; 1876 auto v = v1.peek!(A[]); 1877 //writeln(v[0]); 1878 v1 = [ 9 : v3 ]; 1879 //writeln(v1); 1880 v1 = [ v3 : v3 ]; 1881 //writeln(v1); 1882 } 1883 1884 @system unittest 1885 { 1886 import std.conv : ConvException; 1887 import std.exception : assertThrown, collectException; 1888 // try it with an oddly small size 1889 VariantN!(1) test; 1890 assert(test.size > 1); 1891 1892 // variantArray tests 1893 auto heterogeneous = variantArray(1, 4.5, "hi"); 1894 assert(heterogeneous.length == 3); 1895 auto variantArrayAsVariant = Variant(heterogeneous); 1896 assert(variantArrayAsVariant[0] == 1); 1897 assert(variantArrayAsVariant.length == 3); 1898 1899 // array tests 1900 auto arr = Variant([1.2].dup); 1901 auto e = arr[0]; 1902 assert(e == 1.2); 1903 arr[0] = 2.0; 1904 assert(arr[0] == 2); 1905 arr ~= 4.5; 1906 assert(arr[1] == 4.5); 1907 1908 // general tests 1909 Variant a; 1910 auto b = Variant(5); 1911 assert(!b.peek!(real) && b.peek!(int)); 1912 // assign 1913 a = *b.peek!(int); 1914 // comparison 1915 assert(a == b, a.type.toString() ~ " " ~ b.type.toString()); 1916 auto c = Variant("this is a string"); 1917 assert(a != c); 1918 // comparison via implicit conversions 1919 a = 42; b = 42.0; assert(a == b); 1920 1921 // try failing conversions 1922 bool failed = false; 1923 try 1924 { 1925 auto d = c.get!(int); 1926 } 1927 catch (Exception e) 1928 { 1929 //writeln(stderr, e.toString); 1930 failed = true; 1931 } 1932 assert(failed); // :o) 1933 1934 // toString tests 1935 a = Variant(42); assert(a.toString() == "42"); 1936 a = Variant(42.22); assert(a.toString() == "42.22"); 1937 1938 // coerce tests 1939 a = Variant(42.22); assert(a.coerce!(int) == 42); 1940 a = cast(short) 5; assert(a.coerce!(double) == 5); 1941 a = Variant("10"); assert(a.coerce!int == 10); 1942 1943 a = Variant(1); 1944 assert(a.coerce!bool); 1945 a = Variant(0); 1946 assert(!a.coerce!bool); 1947 1948 a = Variant(1.0); 1949 assert(a.coerce!bool); 1950 a = Variant(0.0); 1951 assert(!a.coerce!bool); 1952 a = Variant(float.init); 1953 assertThrown!ConvException(a.coerce!bool); 1954 1955 a = Variant("true"); 1956 assert(a.coerce!bool); 1957 a = Variant("false"); 1958 assert(!a.coerce!bool); 1959 a = Variant(""); 1960 assertThrown!ConvException(a.coerce!bool); 1961 1962 // Object tests 1963 class B1 {} 1964 class B2 : B1 {} 1965 a = new B2; 1966 assert(a.coerce!(B1) !is null); 1967 a = new B1; 1968 assert(collectException(a.coerce!(B2) is null)); 1969 a = cast(Object) new B2; // lose static type info; should still work 1970 assert(a.coerce!(B2) !is null); 1971 1972 // struct Big { int a[45]; } 1973 // a = Big.init; 1974 1975 // hash 1976 assert(a.toHash() != 0); 1977 } 1978 1979 // tests adapted from 1980 // http://www.dsource.org/projects/tango/browser/trunk/tango/core/Variant.d?rev=2601 1981 @system unittest 1982 { 1983 Variant v; 1984 1985 assert(!v.hasValue); 1986 v = 42; 1987 assert( v.peek!(int) ); 1988 assert( v.convertsTo!(long) ); 1989 assert( v.get!(int) == 42 ); 1990 assert( v.get!(long) == 42L ); 1991 assert( v.get!(ulong) == 42uL ); 1992 1993 v = "Hello, World!"; 1994 assert( v.peek!(string) ); 1995 1996 assert( v.get!(string) == "Hello, World!" ); 1997 assert(!is(char[] : wchar[])); 1998 assert( !v.convertsTo!(wchar[]) ); 1999 assert( v.get!(string) == "Hello, World!" ); 2000 2001 // Literal arrays are dynamically-typed 2002 v = cast(int[4]) [1,2,3,4]; 2003 assert( v.peek!(int[4]) ); 2004 assert( v.get!(int[4]) == [1,2,3,4] ); 2005 2006 { 2007 v = [1,2,3,4,5]; 2008 assert( v.peek!(int[]) ); 2009 assert( v.get!(int[]) == [1,2,3,4,5] ); 2010 } 2011 2012 v = 3.1413; 2013 assert( v.peek!(double) ); 2014 assert( v.convertsTo!(real) ); 2015 //@@@ BUG IN COMPILER: DOUBLE SHOULD NOT IMPLICITLY CONVERT TO FLOAT 2016 assert( v.convertsTo!(float) ); 2017 assert( *v.peek!(double) == 3.1413 ); 2018 2019 auto u = Variant(v); 2020 assert( u.peek!(double) ); 2021 assert( *u.peek!(double) == 3.1413 ); 2022 2023 // operators 2024 v = 38; 2025 assert( v + 4 == 42 ); 2026 assert( 4 + v == 42 ); 2027 assert( v - 4 == 34 ); 2028 assert( Variant(4) - v == -34 ); 2029 assert( v * 2 == 76 ); 2030 assert( 2 * v == 76 ); 2031 assert( v / 2 == 19 ); 2032 assert( Variant(2) / v == 0 ); 2033 assert( v % 2 == 0 ); 2034 assert( Variant(2) % v == 2 ); 2035 assert( (v & 6) == 6 ); 2036 assert( (6 & v) == 6 ); 2037 assert( (v | 9) == 47 ); 2038 assert( (9 | v) == 47 ); 2039 assert( (v ^ 5) == 35 ); 2040 assert( (5 ^ v) == 35 ); 2041 assert( v << 1 == 76 ); 2042 assert( Variant(1) << Variant(2) == 4 ); 2043 assert( v >> 1 == 19 ); 2044 assert( Variant(4) >> Variant(2) == 1 ); 2045 assert( Variant("abc") ~ "def" == "abcdef" ); 2046 assert( Variant("abc") ~ Variant("def") == "abcdef" ); 2047 2048 v = 38; 2049 v += 4; 2050 assert( v == 42 ); 2051 v = 38; v -= 4; assert( v == 34 ); 2052 v = 38; v *= 2; assert( v == 76 ); 2053 v = 38; v /= 2; assert( v == 19 ); 2054 v = 38; v %= 2; assert( v == 0 ); 2055 v = 38; v &= 6; assert( v == 6 ); 2056 v = 38; v |= 9; assert( v == 47 ); 2057 v = 38; v ^= 5; assert( v == 35 ); 2058 v = 38; v <<= 1; assert( v == 76 ); 2059 v = 38; v >>= 1; assert( v == 19 ); 2060 v = 38; v += 1; assert( v < 40 ); 2061 2062 v = "abc"; 2063 v ~= "def"; 2064 assert( v == "abcdef", *v.peek!(char[]) ); 2065 assert( Variant(0) < Variant(42) ); 2066 assert( Variant(42) > Variant(0) ); 2067 assert( Variant(42) > Variant(0.1) ); 2068 assert( Variant(42.1) > Variant(1) ); 2069 assert( Variant(21) == Variant(21) ); 2070 assert( Variant(0) != Variant(42) ); 2071 assert( Variant("bar") == Variant("bar") ); 2072 assert( Variant("foo") != Variant("bar") ); 2073 2074 { 2075 auto v1 = Variant(42); 2076 auto v2 = Variant("foo"); 2077 2078 int[Variant] hash; 2079 hash[v1] = 0; 2080 hash[v2] = 1; 2081 2082 assert( hash[v1] == 0 ); 2083 assert( hash[v2] == 1 ); 2084 } 2085 2086 { 2087 int[char[]] hash; 2088 hash["a"] = 1; 2089 hash["b"] = 2; 2090 hash["c"] = 3; 2091 Variant vhash = hash; 2092 2093 assert( vhash.get!(int[char[]])["a"] == 1 ); 2094 assert( vhash.get!(int[char[]])["b"] == 2 ); 2095 assert( vhash.get!(int[char[]])["c"] == 3 ); 2096 } 2097 } 2098 2099 @system unittest 2100 { 2101 // check comparisons incompatible with AllowedTypes 2102 Algebraic!int v = 2; 2103 2104 assert(v == 2); 2105 assert(v < 3); 2106 static assert(!__traits(compiles, () => v == long.max)); 2107 static assert(!__traits(compiles, () => v == null)); 2108 static assert(!__traits(compiles, () => v < long.max)); 2109 static assert(!__traits(compiles, () => v > null)); 2110 } 2111 2112 // https://issues.dlang.org/show_bug.cgi?id=1558 2113 @system unittest 2114 { 2115 Variant va=1; 2116 Variant vb=-2; 2117 assert((va+vb).get!(int) == -1); 2118 assert((va-vb).get!(int) == 3); 2119 } 2120 2121 @system unittest 2122 { 2123 Variant a; 2124 a=5; 2125 Variant b; 2126 b=a; 2127 Variant[] c; 2128 c = variantArray(1, 2, 3.0, "hello", 4); 2129 assert(c[3] == "hello"); 2130 } 2131 2132 @system unittest 2133 { 2134 Variant v = 5; 2135 assert(!__traits(compiles, v.coerce!(bool delegate()))); 2136 } 2137 2138 2139 @system unittest 2140 { 2141 struct Huge { 2142 real a, b, c, d, e, f, g; 2143 } 2144 2145 Huge huge; 2146 huge.e = 42; 2147 Variant v; 2148 v = huge; // Compile time error. 2149 assert(v.get!(Huge).e == 42); 2150 } 2151 2152 @system unittest 2153 { 2154 const x = Variant(42); 2155 auto y1 = x.get!(const int); 2156 // @@@BUG@@@ 2157 //auto y2 = x.get!(immutable int)(); 2158 } 2159 2160 // test iteration 2161 @system unittest 2162 { 2163 auto v = Variant([ 1, 2, 3, 4 ][]); 2164 auto j = 0; 2165 foreach (int i; v) 2166 { 2167 assert(i == ++j); 2168 } 2169 assert(j == 4); 2170 } 2171 2172 // test convertibility 2173 @system unittest 2174 { 2175 auto v = Variant("abc".dup); 2176 assert(v.convertsTo!(char[])); 2177 } 2178 2179 // https://issues.dlang.org/show_bug.cgi?id=5424 2180 @system unittest 2181 { 2182 interface A { 2183 void func1(); 2184 } 2185 static class AC: A { 2186 void func1() { 2187 } 2188 } 2189 2190 A a = new AC(); 2191 a.func1(); 2192 Variant b = Variant(a); 2193 } 2194 2195 // https://issues.dlang.org/show_bug.cgi?id=7070 2196 @system unittest 2197 { 2198 Variant v; 2199 v = null; 2200 } 2201 2202 // Class and interface opEquals, https://issues.dlang.org/show_bug.cgi?id=12157 2203 @system unittest 2204 { 2205 class Foo { } 2206 2207 class DerivedFoo : Foo { } 2208 2209 Foo f1 = new Foo(); 2210 Foo f2 = new DerivedFoo(); 2211 2212 Variant v1 = f1, v2 = f2; 2213 assert(v1 == f1); 2214 assert(v1 != new Foo()); 2215 assert(v1 != f2); 2216 assert(v2 != v1); 2217 assert(v2 == f2); 2218 } 2219 2220 // Const parameters with opCall, https://issues.dlang.org/show_bug.cgi?id=11361 2221 @system unittest 2222 { 2223 static string t1(string c) { 2224 return c ~ "a"; 2225 } 2226 2227 static const(char)[] t2(const(char)[] p) { 2228 return p ~ "b"; 2229 } 2230 2231 static char[] t3(int p) { 2232 import std.conv : text; 2233 return p.text.dup; 2234 } 2235 2236 Variant v1 = &t1; 2237 Variant v2 = &t2; 2238 Variant v3 = &t3; 2239 2240 assert(v1("abc") == "abca"); 2241 assert(v1("abc").type == typeid(string)); 2242 assert(v2("abc") == "abcb"); 2243 2244 assert(v2(cast(char[])("abc".dup)) == "abcb"); 2245 assert(v2("abc").type == typeid(const(char)[])); 2246 2247 assert(v3(4) == ['4']); 2248 assert(v3(4).type == typeid(char[])); 2249 } 2250 2251 // https://issues.dlang.org/show_bug.cgi?id=12071 2252 @system unittest 2253 { 2254 static struct Structure { int data; } 2255 alias VariantTest = Algebraic!(Structure delegate() pure nothrow @nogc @safe); 2256 2257 bool called = false; 2258 Structure example() pure nothrow @nogc @safe 2259 { 2260 called = true; 2261 return Structure.init; 2262 } 2263 auto m = VariantTest(&example); 2264 m(); 2265 assert(called); 2266 } 2267 2268 // Ordering comparisons of incompatible types 2269 // e.g. https://issues.dlang.org/show_bug.cgi?id=7990 2270 @system unittest 2271 { 2272 import std.exception : assertThrown; 2273 assertThrown!VariantException(Variant(3) < "a"); 2274 assertThrown!VariantException("a" < Variant(3)); 2275 assertThrown!VariantException(Variant(3) < Variant("a")); 2276 2277 assertThrown!VariantException(Variant.init < Variant(3)); 2278 assertThrown!VariantException(Variant(3) < Variant.init); 2279 } 2280 2281 // Handling of unordered types 2282 // https://issues.dlang.org/show_bug.cgi?id=9043 2283 @system unittest 2284 { 2285 import std.exception : assertThrown; 2286 static struct A { int a; } 2287 2288 assert(Variant(A(3)) != A(4)); 2289 2290 assertThrown!VariantException(Variant(A(3)) < A(4)); 2291 assertThrown!VariantException(A(3) < Variant(A(4))); 2292 assertThrown!VariantException(Variant(A(3)) < Variant(A(4))); 2293 } 2294 2295 // Handling of empty types and arrays 2296 // https://issues.dlang.org/show_bug.cgi?id=10958 2297 @system unittest 2298 { 2299 class EmptyClass { } 2300 struct EmptyStruct { } 2301 alias EmptyArray = void[0]; 2302 alias Alg = Algebraic!(EmptyClass, EmptyStruct, EmptyArray); 2303 2304 Variant testEmpty(T)() 2305 { 2306 T inst; 2307 Variant v = inst; 2308 assert(v.get!T == inst); 2309 assert(v.peek!T !is null); 2310 assert(*v.peek!T == inst); 2311 Alg alg = inst; 2312 assert(alg.get!T == inst); 2313 return v; 2314 } 2315 2316 testEmpty!EmptyClass(); 2317 testEmpty!EmptyStruct(); 2318 testEmpty!EmptyArray(); 2319 2320 // EmptyClass/EmptyStruct sizeof is 1, so we have this to test just size 0. 2321 EmptyArray arr = EmptyArray.init; 2322 Algebraic!(EmptyArray) a = arr; 2323 assert(a.length == 0); 2324 assert(a.get!EmptyArray == arr); 2325 } 2326 2327 // Handling of void function pointers / delegates 2328 // https://issues.dlang.org/show_bug.cgi?id=11360 2329 @system unittest 2330 { 2331 static void t1() { } 2332 Variant v = &t1; 2333 assert(v() == Variant.init); 2334 2335 static int t2() { return 3; } 2336 Variant v2 = &t2; 2337 assert(v2() == 3); 2338 } 2339 2340 // Using peek for large structs 2341 // https://issues.dlang.org/show_bug.cgi?id=8580 2342 @system unittest 2343 { 2344 struct TestStruct(bool pad) 2345 { 2346 int val1; 2347 static if (pad) 2348 ubyte[Variant.size] padding; 2349 int val2; 2350 } 2351 2352 void testPeekWith(T)() 2353 { 2354 T inst; 2355 inst.val1 = 3; 2356 inst.val2 = 4; 2357 Variant v = inst; 2358 T* original = v.peek!T; 2359 assert(original.val1 == 3); 2360 assert(original.val2 == 4); 2361 original.val1 = 6; 2362 original.val2 = 8; 2363 T modified = v.get!T; 2364 assert(modified.val1 == 6); 2365 assert(modified.val2 == 8); 2366 } 2367 2368 testPeekWith!(TestStruct!false)(); 2369 testPeekWith!(TestStruct!true)(); 2370 } 2371 2372 // https://issues.dlang.org/show_bug.cgi?id=18780 2373 @system unittest 2374 { 2375 int x = 7; 2376 Variant a = x; 2377 assert(a.convertsTo!ulong); 2378 assert(a.convertsTo!uint); 2379 } 2380 2381 /** 2382 * Applies a delegate or function to the given $(LREF Algebraic) depending on the held type, 2383 * ensuring that all types are handled by the visiting functions. 2384 * 2385 * The delegate or function having the currently held value as parameter is called 2386 * with `variant`'s current value. Visiting handlers are passed 2387 * in the template parameter list. 2388 * It is statically ensured that all held types of 2389 * `variant` are handled across all handlers. 2390 * `visit` allows delegates and static functions to be passed 2391 * as parameters. 2392 * 2393 * If a function with an untyped parameter is specified, this function is called 2394 * when the variant contains a type that does not match any other function. 2395 * This can be used to apply the same function across multiple possible types. 2396 * Exactly one generic function is allowed. 2397 * 2398 * If a function without parameters is specified, this function is called 2399 * when `variant` doesn't hold a value. Exactly one parameter-less function 2400 * is allowed. 2401 * 2402 * Duplicate overloads matching the same type in one of the visitors are disallowed. 2403 * 2404 * Returns: The return type of visit is deduced from the visiting functions and must be 2405 * the same across all overloads. 2406 * Throws: $(LREF VariantException) if `variant` doesn't hold a value and no 2407 * parameter-less fallback function is specified. 2408 */ 2409 template visit(Handlers...) 2410 if (Handlers.length > 0) 2411 { 2412 /// 2413 auto visit(VariantType)(VariantType variant) 2414 if (isAlgebraic!VariantType) 2415 { 2416 return visitImpl!(true, VariantType, Handlers)(variant); 2417 } 2418 } 2419 2420 /// 2421 @system unittest 2422 { 2423 Algebraic!(int, string) variant; 2424 2425 variant = 10; 2426 assert(variant.visit!((string s) => cast(int) s.length, 2427 (int i) => i)() 2428 == 10); 2429 variant = "string"; 2430 assert(variant.visit!((int i) => i, 2431 (string s) => cast(int) s.length)() 2432 == 6); 2433 2434 // Error function usage 2435 Algebraic!(int, string) emptyVar; 2436 auto rslt = emptyVar.visit!((string s) => cast(int) s.length, 2437 (int i) => i, 2438 () => -1)(); 2439 assert(rslt == -1); 2440 2441 // Generic function usage 2442 Algebraic!(int, float, real) number = 2; 2443 assert(number.visit!(x => x += 1) == 3); 2444 2445 // Generic function for int/float with separate behavior for string 2446 Algebraic!(int, float, string) something = 2; 2447 assert(something.visit!((string s) => s.length, x => x) == 2); // generic 2448 something = "asdf"; 2449 assert(something.visit!((string s) => s.length, x => x) == 4); // string 2450 2451 // Generic handler and empty handler 2452 Algebraic!(int, float, real) empty2; 2453 assert(empty2.visit!(x => x + 1, () => -1) == -1); 2454 } 2455 2456 @system unittest 2457 { 2458 Algebraic!(size_t, string) variant; 2459 2460 // not all handled check 2461 static assert(!__traits(compiles, variant.visit!((size_t i){ })() )); 2462 2463 variant = cast(size_t) 10; 2464 auto which = 0; 2465 variant.visit!( (string s) => which = 1, 2466 (size_t i) => which = 0 2467 )(); 2468 2469 // integer overload was called 2470 assert(which == 0); 2471 2472 // mustn't compile as generic Variant not supported 2473 Variant v; 2474 static assert(!__traits(compiles, v.visit!((string s) => which = 1, 2475 (size_t i) => which = 0 2476 )() 2477 )); 2478 2479 static size_t func(string s) { 2480 return s.length; 2481 } 2482 2483 variant = "test"; 2484 assert( 4 == variant.visit!(func, 2485 (size_t i) => i 2486 )()); 2487 2488 Algebraic!(int, float, string) variant2 = 5.0f; 2489 // Shouldn' t compile as float not handled by visitor. 2490 static assert(!__traits(compiles, variant2.visit!( 2491 (int _) {}, 2492 (string _) {})())); 2493 2494 Algebraic!(size_t, string, float) variant3; 2495 variant3 = 10.0f; 2496 auto floatVisited = false; 2497 2498 assert(variant3.visit!( 2499 (float f) { floatVisited = true; return cast(size_t) f; }, 2500 func, 2501 (size_t i) { return i; } 2502 )() == 10); 2503 assert(floatVisited == true); 2504 2505 Algebraic!(float, string) variant4; 2506 2507 assert(variant4.visit!(func, (float f) => cast(size_t) f, () => size_t.max)() == size_t.max); 2508 2509 // double error func check 2510 static assert(!__traits(compiles, 2511 visit!(() => size_t.max, func, (float f) => cast(size_t) f, () => size_t.max)(variant4)) 2512 ); 2513 } 2514 2515 // disallow providing multiple generic handlers to visit 2516 // disallow a generic handler that does not apply to all types 2517 @system unittest 2518 { 2519 Algebraic!(int, float) number = 2; 2520 // ok, x + 1 valid for int and float 2521 static assert( __traits(compiles, number.visit!(x => x + 1))); 2522 // bad, two generic handlers 2523 static assert(!__traits(compiles, number.visit!(x => x + 1, x => x + 2))); 2524 // bad, x ~ "a" does not apply to int or float 2525 static assert(!__traits(compiles, number.visit!(x => x ~ "a"))); 2526 // bad, x ~ "a" does not apply to int or float 2527 static assert(!__traits(compiles, number.visit!(x => x + 1, x => x ~ "a"))); 2528 2529 Algebraic!(int, string) maybenumber = 2; 2530 // ok, x ~ "a" valid for string, x + 1 valid for int, only 1 generic 2531 static assert( __traits(compiles, maybenumber.visit!((string x) => x ~ "a", x => "foobar"[0 .. x + 1]))); 2532 // bad, x ~ "a" valid for string but not int 2533 static assert(!__traits(compiles, maybenumber.visit!(x => x ~ "a"))); 2534 // bad, two generics, each only applies in one case 2535 static assert(!__traits(compiles, maybenumber.visit!(x => x + 1, x => x ~ "a"))); 2536 } 2537 2538 /** 2539 * Behaves as $(LREF visit) but doesn't enforce that all types are handled 2540 * by the visiting functions. 2541 * 2542 * If a parameter-less function is specified it is called when 2543 * either `variant` doesn't hold a value or holds a type 2544 * which isn't handled by the visiting functions. 2545 * 2546 * Returns: The return type of tryVisit is deduced from the visiting functions and must be 2547 * the same across all overloads. 2548 * Throws: $(LREF VariantException) if `variant` doesn't hold a value or 2549 * `variant` holds a value which isn't handled by the visiting functions, 2550 * when no parameter-less fallback function is specified. 2551 */ 2552 template tryVisit(Handlers...) 2553 if (Handlers.length > 0) 2554 { 2555 /// 2556 auto tryVisit(VariantType)(VariantType variant) 2557 if (isAlgebraic!VariantType) 2558 { 2559 return visitImpl!(false, VariantType, Handlers)(variant); 2560 } 2561 } 2562 2563 /// 2564 @system unittest 2565 { 2566 Algebraic!(int, string) variant; 2567 2568 variant = 10; 2569 auto which = -1; 2570 variant.tryVisit!((int i) { which = 0; })(); 2571 assert(which == 0); 2572 2573 // Error function usage 2574 variant = "test"; 2575 variant.tryVisit!((int i) { which = 0; }, 2576 () { which = -100; })(); 2577 assert(which == -100); 2578 } 2579 2580 @system unittest 2581 { 2582 import std.exception : assertThrown; 2583 Algebraic!(int, string) variant; 2584 2585 variant = 10; 2586 auto which = -1; 2587 variant.tryVisit!((int i){ which = 0; })(); 2588 2589 assert(which == 0); 2590 2591 variant = "test"; 2592 2593 assertThrown!VariantException(variant.tryVisit!((int i) { which = 0; })()); 2594 2595 void errorfunc() 2596 { 2597 which = -1; 2598 } 2599 2600 variant.tryVisit!((int i) { which = 0; }, errorfunc)(); 2601 2602 assert(which == -1); 2603 } 2604 2605 private template isAlgebraic(Type) 2606 { 2607 static if (is(Type _ == VariantN!T, T...)) 2608 enum isAlgebraic = T.length >= 2; // T[0] == maxDataSize, T[1..$] == AllowedTypesParam 2609 else 2610 enum isAlgebraic = false; 2611 } 2612 2613 @system unittest 2614 { 2615 static assert(!isAlgebraic!(Variant)); 2616 static assert( isAlgebraic!(Algebraic!(string))); 2617 static assert( isAlgebraic!(Algebraic!(int, int[]))); 2618 } 2619 2620 private auto visitImpl(bool Strict, VariantType, Handler...)(VariantType variant) 2621 if (isAlgebraic!VariantType && Handler.length > 0) 2622 { 2623 alias AllowedTypes = VariantType.AllowedTypes; 2624 2625 2626 /** 2627 * Returns: Struct where `indices` is an array which 2628 * contains at the n-th position the index in Handler which takes the 2629 * n-th type of AllowedTypes. If an Handler doesn't match an 2630 * AllowedType, -1 is set. If a function in the delegates doesn't 2631 * have parameters, the field `exceptionFuncIdx` is set; 2632 * otherwise it's -1. 2633 */ 2634 auto visitGetOverloadMap() 2635 { 2636 struct Result { 2637 int[AllowedTypes.length] indices; 2638 int exceptionFuncIdx = -1; 2639 int generalFuncIdx = -1; 2640 } 2641 2642 Result result; 2643 2644 enum int nonmatch = () 2645 { 2646 foreach (int dgidx, dg; Handler) 2647 { 2648 bool found = false; 2649 foreach (T; AllowedTypes) 2650 { 2651 found |= __traits(compiles, { static assert(isSomeFunction!(dg!T)); }); 2652 found |= __traits(compiles, (T t) { dg(t); }); 2653 found |= __traits(compiles, dg()); 2654 } 2655 if (!found) return dgidx; 2656 } 2657 return -1; 2658 }(); 2659 static assert(nonmatch == -1, "No match for visit handler #"~ 2660 nonmatch.stringof~" ("~Handler[nonmatch].stringof~")"); 2661 2662 foreach (tidx, T; AllowedTypes) 2663 { 2664 bool added = false; 2665 foreach (dgidx, dg; Handler) 2666 { 2667 // Handle normal function objects 2668 static if (isSomeFunction!dg) 2669 { 2670 alias Params = Parameters!dg; 2671 static if (Params.length == 0) 2672 { 2673 // Just check exception functions in the first 2674 // inner iteration (over delegates) 2675 if (tidx > 0) 2676 continue; 2677 else 2678 { 2679 if (result.exceptionFuncIdx != -1) 2680 assert(false, "duplicate parameter-less (error-)function specified"); 2681 result.exceptionFuncIdx = dgidx; 2682 } 2683 } 2684 else static if (is(Params[0] == T) || is(Unqual!(Params[0]) == T)) 2685 { 2686 if (added) 2687 assert(false, "duplicate overload specified for type '" ~ T.stringof ~ "'"); 2688 2689 added = true; 2690 result.indices[tidx] = dgidx; 2691 } 2692 } 2693 else static if (__traits(compiles, { static assert(isSomeFunction!(dg!T)); })) 2694 { 2695 assert(result.generalFuncIdx == -1 || 2696 result.generalFuncIdx == dgidx, 2697 "Only one generic visitor function is allowed"); 2698 result.generalFuncIdx = dgidx; 2699 } 2700 // Handle composite visitors with opCall overloads 2701 } 2702 2703 if (!added) 2704 result.indices[tidx] = -1; 2705 } 2706 2707 return result; 2708 } 2709 2710 enum HandlerOverloadMap = visitGetOverloadMap(); 2711 2712 if (!variant.hasValue) 2713 { 2714 // Call the exception function. The HandlerOverloadMap 2715 // will have its exceptionFuncIdx field set to value != -1 if an 2716 // exception function has been specified; otherwise we just through an exception. 2717 static if (HandlerOverloadMap.exceptionFuncIdx != -1) 2718 return Handler[ HandlerOverloadMap.exceptionFuncIdx ](); 2719 else 2720 throw new VariantException("variant must hold a value before being visited."); 2721 } 2722 2723 foreach (idx, T; AllowedTypes) 2724 { 2725 if (auto ptr = variant.peek!T) 2726 { 2727 enum dgIdx = HandlerOverloadMap.indices[idx]; 2728 2729 static if (dgIdx == -1) 2730 { 2731 static if (HandlerOverloadMap.generalFuncIdx >= 0) 2732 return Handler[HandlerOverloadMap.generalFuncIdx](*ptr); 2733 else static if (Strict) 2734 static assert(false, "overload for type '" ~ T.stringof ~ "' hasn't been specified"); 2735 else static if (HandlerOverloadMap.exceptionFuncIdx != -1) 2736 return Handler[HandlerOverloadMap.exceptionFuncIdx](); 2737 else 2738 throw new VariantException( 2739 "variant holds value of type '" 2740 ~ T.stringof ~ 2741 "' but no visitor has been provided" 2742 ); 2743 } 2744 else 2745 { 2746 return Handler[ dgIdx ](*ptr); 2747 } 2748 } 2749 } 2750 2751 assert(false); 2752 } 2753 2754 // https://issues.dlang.org/show_bug.cgi?id=21253 2755 @system unittest 2756 { 2757 static struct A { int n; } 2758 static struct B { } 2759 2760 auto a = Algebraic!(A, B)(B()); 2761 assert(a.visit!( 2762 (B _) => 42, 2763 (a ) => a.n 2764 ) == 42); 2765 } 2766 2767 @system unittest 2768 { 2769 // validate that visit can be called with a const type 2770 struct Foo { int depth; } 2771 struct Bar { int depth; } 2772 alias FooBar = Algebraic!(Foo, Bar); 2773 2774 int depth(in FooBar fb) { 2775 return fb.visit!((Foo foo) => foo.depth, 2776 (Bar bar) => bar.depth); 2777 } 2778 2779 FooBar fb = Foo(3); 2780 assert(depth(fb) == 3); 2781 } 2782 2783 // https://issues.dlang.org/show_bug.cgi?id=16383 2784 @system unittest 2785 { 2786 class Foo {this() immutable {}} 2787 alias V = Algebraic!(immutable Foo); 2788 2789 auto x = V(new immutable Foo).visit!( 2790 (immutable(Foo) _) => 3 2791 ); 2792 assert(x == 3); 2793 } 2794 2795 // https://issues.dlang.org/show_bug.cgi?id=5310 2796 @system unittest 2797 { 2798 const Variant a; 2799 assert(a == a); 2800 Variant b; 2801 assert(a == b); 2802 assert(b == a); 2803 } 2804 2805 @system unittest 2806 { 2807 const Variant a = [2]; 2808 assert(a[0] == 2); 2809 } 2810 2811 // https://issues.dlang.org/show_bug.cgi?id=10017 2812 @system unittest 2813 { 2814 static struct S 2815 { 2816 ubyte[Variant.size + 1] s; 2817 } 2818 2819 Variant v1, v2; 2820 v1 = S(); // the payload is allocated on the heap 2821 v2 = v1; // AssertError: target must be non-null 2822 assert(v1 == v2); 2823 } 2824 2825 // https://issues.dlang.org/show_bug.cgi?id=7069 2826 @system unittest 2827 { 2828 import std.exception : assertThrown; 2829 Variant v; 2830 2831 int i = 10; 2832 v = i; 2833 static foreach (qual; AliasSeq!(Alias, ConstOf)) 2834 { 2835 assert(v.get!(qual!int) == 10); 2836 assert(v.get!(qual!float) == 10.0f); 2837 } 2838 static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) 2839 { 2840 assertThrown!VariantException(v.get!(qual!int)); 2841 } 2842 2843 const(int) ci = 20; 2844 v = ci; 2845 static foreach (qual; AliasSeq!(ConstOf)) 2846 { 2847 assert(v.get!(qual!int) == 20); 2848 assert(v.get!(qual!float) == 20.0f); 2849 } 2850 static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf)) 2851 { 2852 assertThrown!VariantException(v.get!(qual!int)); 2853 assertThrown!VariantException(v.get!(qual!float)); 2854 } 2855 2856 immutable(int) ii = ci; 2857 v = ii; 2858 static foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf)) 2859 { 2860 assert(v.get!(qual!int) == 20); 2861 assert(v.get!(qual!float) == 20.0f); 2862 } 2863 static foreach (qual; AliasSeq!(Alias, SharedOf)) 2864 { 2865 assertThrown!VariantException(v.get!(qual!int)); 2866 assertThrown!VariantException(v.get!(qual!float)); 2867 } 2868 2869 int[] ai = [1,2,3]; 2870 v = ai; 2871 static foreach (qual; AliasSeq!(Alias, ConstOf)) 2872 { 2873 assert(v.get!(qual!(int[])) == [1,2,3]); 2874 assert(v.get!(qual!(int)[]) == [1,2,3]); 2875 } 2876 static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) 2877 { 2878 assertThrown!VariantException(v.get!(qual!(int[]))); 2879 assertThrown!VariantException(v.get!(qual!(int)[])); 2880 } 2881 2882 const(int[]) cai = [4,5,6]; 2883 v = cai; 2884 static foreach (qual; AliasSeq!(ConstOf)) 2885 { 2886 assert(v.get!(qual!(int[])) == [4,5,6]); 2887 assert(v.get!(qual!(int)[]) == [4,5,6]); 2888 } 2889 static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf)) 2890 { 2891 assertThrown!VariantException(v.get!(qual!(int[]))); 2892 assertThrown!VariantException(v.get!(qual!(int)[])); 2893 } 2894 2895 immutable(int[]) iai = [7,8,9]; 2896 v = iai; 2897 //assert(v.get!(immutable(int[])) == [7,8,9]); // Bug ??? runtime error 2898 assert(v.get!(immutable(int)[]) == [7,8,9]); 2899 assert(v.get!(const(int[])) == [7,8,9]); 2900 assert(v.get!(const(int)[]) == [7,8,9]); 2901 //assert(v.get!(shared(const(int[]))) == cast(shared const)[7,8,9]); // Bug ??? runtime error 2902 //assert(v.get!(shared(const(int))[]) == cast(shared const)[7,8,9]); // Bug ??? runtime error 2903 static foreach (qual; AliasSeq!(Alias)) 2904 { 2905 assertThrown!VariantException(v.get!(qual!(int[]))); 2906 assertThrown!VariantException(v.get!(qual!(int)[])); 2907 } 2908 2909 class A {} 2910 class B : A {} 2911 B b = new B(); 2912 v = b; 2913 static foreach (qual; AliasSeq!(Alias, ConstOf)) 2914 { 2915 assert(v.get!(qual!B) is b); 2916 assert(v.get!(qual!A) is b); 2917 assert(v.get!(qual!Object) is b); 2918 } 2919 static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) 2920 { 2921 assertThrown!VariantException(v.get!(qual!B)); 2922 assertThrown!VariantException(v.get!(qual!A)); 2923 assertThrown!VariantException(v.get!(qual!Object)); 2924 } 2925 2926 const(B) cb = new B(); 2927 v = cb; 2928 static foreach (qual; AliasSeq!(ConstOf)) 2929 { 2930 assert(v.get!(qual!B) is cb); 2931 assert(v.get!(qual!A) is cb); 2932 assert(v.get!(qual!Object) is cb); 2933 } 2934 static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf)) 2935 { 2936 assertThrown!VariantException(v.get!(qual!B)); 2937 assertThrown!VariantException(v.get!(qual!A)); 2938 assertThrown!VariantException(v.get!(qual!Object)); 2939 } 2940 2941 immutable(B) ib = new immutable(B)(); 2942 v = ib; 2943 static foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf)) 2944 { 2945 assert(v.get!(qual!B) is ib); 2946 assert(v.get!(qual!A) is ib); 2947 assert(v.get!(qual!Object) is ib); 2948 } 2949 static foreach (qual; AliasSeq!(Alias, SharedOf)) 2950 { 2951 assertThrown!VariantException(v.get!(qual!B)); 2952 assertThrown!VariantException(v.get!(qual!A)); 2953 assertThrown!VariantException(v.get!(qual!Object)); 2954 } 2955 2956 shared(B) sb = new shared B(); 2957 v = sb; 2958 static foreach (qual; AliasSeq!(SharedOf, SharedConstOf)) 2959 { 2960 assert(v.get!(qual!B) is sb); 2961 assert(v.get!(qual!A) is sb); 2962 assert(v.get!(qual!Object) is sb); 2963 } 2964 static foreach (qual; AliasSeq!(Alias, ImmutableOf, ConstOf)) 2965 { 2966 assertThrown!VariantException(v.get!(qual!B)); 2967 assertThrown!VariantException(v.get!(qual!A)); 2968 assertThrown!VariantException(v.get!(qual!Object)); 2969 } 2970 2971 shared(const(B)) scb = new shared const B(); 2972 v = scb; 2973 static foreach (qual; AliasSeq!(SharedConstOf)) 2974 { 2975 assert(v.get!(qual!B) is scb); 2976 assert(v.get!(qual!A) is scb); 2977 assert(v.get!(qual!Object) is scb); 2978 } 2979 static foreach (qual; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) 2980 { 2981 assertThrown!VariantException(v.get!(qual!B)); 2982 assertThrown!VariantException(v.get!(qual!A)); 2983 assertThrown!VariantException(v.get!(qual!Object)); 2984 } 2985 } 2986 2987 // https://issues.dlang.org/show_bug.cgi?id=12540 2988 @system unittest 2989 { 2990 static struct DummyScope 2991 { 2992 alias Alias12540 = Algebraic!Class12540; 2993 2994 static class Class12540 2995 { 2996 Alias12540 entity; 2997 } 2998 } 2999 } 3000 3001 @system unittest 3002 { 3003 // https://issues.dlang.org/show_bug.cgi?id=10194 3004 // Also test for elaborate copying 3005 static struct S 3006 { 3007 @disable this(); 3008 this(int dummy) 3009 { 3010 ++cnt; 3011 } 3012 3013 this(this) 3014 { 3015 ++cnt; 3016 } 3017 3018 @disable S opAssign(); 3019 3020 ~this() 3021 { 3022 --cnt; 3023 assert(cnt >= 0); 3024 } 3025 static int cnt = 0; 3026 } 3027 3028 { 3029 Variant v; 3030 { 3031 v = S(0); 3032 assert(S.cnt == 1); 3033 } 3034 assert(S.cnt == 1); 3035 3036 // assigning a new value should destroy the existing one 3037 v = 0; 3038 assert(S.cnt == 0); 3039 3040 // destroying the variant should destroy it's current value 3041 v = S(0); 3042 assert(S.cnt == 1); 3043 } 3044 assert(S.cnt == 0); 3045 } 3046 3047 @system unittest 3048 { 3049 // https://issues.dlang.org/show_bug.cgi?id=13300 3050 static struct S 3051 { 3052 this(this) {} 3053 ~this() {} 3054 } 3055 3056 static assert( hasElaborateCopyConstructor!(Variant)); 3057 static assert(!hasElaborateCopyConstructor!(Algebraic!bool)); 3058 static assert( hasElaborateCopyConstructor!(Algebraic!S)); 3059 static assert( hasElaborateCopyConstructor!(Algebraic!(bool, S))); 3060 3061 static assert( hasElaborateDestructor!(Variant)); 3062 static assert(!hasElaborateDestructor!(Algebraic!bool)); 3063 static assert( hasElaborateDestructor!(Algebraic!S)); 3064 static assert( hasElaborateDestructor!(Algebraic!(bool, S))); 3065 3066 import std.array; 3067 alias Value = Algebraic!bool; 3068 3069 static struct T 3070 { 3071 Value value; 3072 @disable this(); 3073 } 3074 auto a = appender!(T[]); 3075 } 3076 3077 // https://issues.dlang.org/show_bug.cgi?id=13871 3078 @system unittest 3079 { 3080 alias A = Algebraic!(int, typeof(null)); 3081 static struct B { A value; } 3082 alias C = std.variant.Algebraic!B; 3083 3084 C var; 3085 var = C(B()); 3086 } 3087 3088 @system unittest 3089 { 3090 import std.exception : assertThrown, assertNotThrown; 3091 // Make sure Variant can handle types with opDispatch but no length field. 3092 struct SWithNoLength 3093 { 3094 void opDispatch(string s)() { } 3095 } 3096 3097 struct SWithLength 3098 { 3099 @property int opDispatch(string s)() 3100 { 3101 // Assume that s == "length" 3102 return 5; // Any value is OK for test. 3103 } 3104 } 3105 3106 SWithNoLength sWithNoLength; 3107 Variant v = sWithNoLength; 3108 assertThrown!VariantException(v.length); 3109 3110 SWithLength sWithLength; 3111 v = sWithLength; 3112 assertNotThrown!VariantException(v.get!SWithLength.length); 3113 assertThrown!VariantException(v.length); 3114 } 3115 3116 // https://issues.dlang.org/show_bug.cgi?id=13534 3117 @system unittest 3118 { 3119 static assert(!__traits(compiles, () @safe { 3120 auto foo() @system { return 3; } 3121 auto v = Variant(&foo); 3122 v(); // foo is called in safe code!? 3123 })); 3124 } 3125 3126 // https://issues.dlang.org/show_bug.cgi?id=15039 3127 @system unittest 3128 { 3129 import std.typecons; 3130 import std.variant; 3131 3132 alias IntTypedef = Typedef!int; 3133 alias Obj = Algebraic!(int, IntTypedef, This[]); 3134 3135 Obj obj = 1; 3136 3137 obj.visit!( 3138 (int x) {}, 3139 (IntTypedef x) {}, 3140 (Obj[] x) {}, 3141 ); 3142 } 3143 3144 // https://issues.dlang.org/show_bug.cgi?id=15791 3145 @system unittest 3146 { 3147 int n = 3; 3148 struct NS1 { int foo() { return n + 10; } } 3149 struct NS2 { int foo() { return n * 10; } } 3150 3151 Variant v; 3152 v = NS1(); 3153 assert(v.get!NS1.foo() == 13); 3154 v = NS2(); 3155 assert(v.get!NS2.foo() == 30); 3156 } 3157 3158 // https://issues.dlang.org/show_bug.cgi?id=15827 3159 @system unittest 3160 { 3161 static struct Foo15827 { Variant v; this(Foo15827 v) {} } 3162 Variant v = Foo15827.init; 3163 } 3164 3165 // https://issues.dlang.org/show_bug.cgi?id=18934 3166 @system unittest 3167 { 3168 static struct S 3169 { 3170 const int x; 3171 } 3172 3173 auto s = S(42); 3174 Variant v = s; 3175 auto s2 = v.get!S; 3176 assert(s2.x == 42); 3177 Variant v2 = v; // support copying from one variant to the other 3178 v2 = S(2); 3179 v = v2; 3180 assert(v.get!S.x == 2); 3181 } 3182 3183 // https://issues.dlang.org/show_bug.cgi?id=19200 3184 @system unittest 3185 { 3186 static struct S 3187 { 3188 static int opBinaryRight(string op : "|", T)(T rhs) 3189 { 3190 return 3; 3191 } 3192 } 3193 3194 S s; 3195 Variant v; 3196 auto b = v | s; 3197 assert(b == 3); 3198 } 3199 3200 // https://issues.dlang.org/show_bug.cgi?id=11061 3201 @system unittest 3202 { 3203 int[4] el = [0, 1, 2, 3]; 3204 int[3] nl = [0, 1, 2]; 3205 Variant v1 = el; 3206 assert(v1 == el); // Compare Var(static) to static 3207 assert(v1 != nl); // Compare static arrays of different length 3208 assert(v1 == [0, 1, 2, 3]); // Compare Var(static) to dynamic. 3209 assert(v1 != [0, 1, 2]); 3210 int[] dyn = [0, 1, 2, 3]; 3211 v1 = dyn; 3212 assert(v1 == el); // Compare Var(dynamic) to static. 3213 assert(v1 == [0, 1] ~ [2, 3]); // Compare Var(dynamic) to dynamic 3214 } 3215 3216 // https://issues.dlang.org/show_bug.cgi?id=15940 3217 @system unittest 3218 { 3219 class C { } 3220 struct S 3221 { 3222 C a; 3223 alias a this; 3224 } 3225 S s = S(new C()); 3226 auto v = Variant(s); // compile error 3227 } 3228 3229 @system unittest 3230 { 3231 // Test if we don't have scoping issues. 3232 Variant createVariant(int[] input) 3233 { 3234 int[2] el = [input[0], input[1]]; 3235 Variant v = el; 3236 return v; 3237 } 3238 Variant v = createVariant([0, 1]); 3239 createVariant([2, 3]); 3240 assert(v == [0,1]); 3241 } 3242 3243 // https://issues.dlang.org/show_bug.cgi?id=19994 3244 @safe unittest 3245 { 3246 alias Inner = Algebraic!(This*); 3247 alias Outer = Algebraic!(Inner, This*); 3248 3249 static assert(is(Outer.AllowedTypes == AliasSeq!(Inner, Outer*))); 3250 } 3251 3252 // https://issues.dlang.org/show_bug.cgi?id=21296 3253 @system unittest 3254 { 3255 immutable aa = ["0": 0]; 3256 auto v = Variant(aa); // compile error 3257 }