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