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