1 /++ 2 $(H2 Variant and Nullable types) 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 The module defines generic $(LREF Algebraic) type that contains a payload. 14 The allowed types of the paylad are defined by the unordered $(LREF TypeSet). 15 16 $(LREF Algebraic) template accepts two arguments: self type set id and a list of type sets. 17 18 $(BOOKTABLE $(H3 $(LREF Algebraic) Aliases), 19 $(TR $(TH Name) $(TH Description)) 20 $(T2 Variant, an algebraic type) 21 $(T2 TaggedVariant, a tagged algebraic type) 22 $(T2 Nullable, an algebraic type with at least `typeof(null)`) 23 ) 24 25 $(BOOKTABLE $(H3 Visitor Handlers), 26 $(TR $(TH Name) $(TH Ensures can match) $(TH Throws if no match) $(TH Returns $(LREF Nullable)) $(TH Multiple dispatch) $(TH Argumments count) $(TH Algebraic first argument) $(TH Fuses Algebraic types on return)) 27 $(LEADINGROWN 8, Classic handlers) 28 $(T8 visit, Yes, N/A, No, No, 1+, Yes, No) 29 $(T8 optionalVisit, No, No, Yes, No, 1+, Yes, No) 30 $(T8 autoVisit, No, No, auto, No, 1+, Yes, No) 31 $(T8 tryVisit, No, Yes, No, No, 1+, Yes, No) 32 $(LEADINGROWN 8, Multiple dispatch and algebraic fusion on return) 33 $(T8 match, Yes, N/A, No, Yes, 0+, auto, Yes) 34 $(T8 optionalMatch, No, No, Yes, Yes, 0+, auto, Yes) 35 $(T8 autoMatch, No, No, auto, Yes, 0+, auto, Yes) 36 $(T8 tryMatch, No, Yes, No, Yes, 0+, auto, Yes) 37 $(LEADINGROWN 8, Member access) 38 $(T8 getMember, Yes, N/A, No, No, 1+, Yes, No) 39 $(T8 optionalGetMember, No, No, Yes, No, 1+, Yes, No) 40 $(T8 autoGetMember, No, No, auto, No, 1+, Yes, No) 41 $(T8 tryGetMember, No, Yes, No, No, 1+, Yes, No) 42 $(LEADINGROWN 8, Member access with algebraic fusion on return) 43 $(T8 matchMember, Yes, N/A, No, No, 1+, Yes, Yes) 44 $(T8 optionalMatchMember, No, No, Yes, No, 1+, Yes, Yes) 45 $(T8 autoMatchMember, No, No, auto, No, 1+, Yes, Yes) 46 $(T8 tryMatchMember, No, Yes, No, No, 1+, Yes, Yes) 47 ) 48 49 $(BOOKTABLE $(H3 Special Types), 50 $(TR $(TH Name) $(TH Description)) 51 $(T2plain `void`, It is usefull to indicate a possible return type of the visitor. Can't be accesed by reference. ) 52 $(T2plain `typeof(null)`, It is usefull for nullable types. Also, it is used to indicate that a visitor can't match the current value of the algebraic. Can't be accesed by reference. ) 53 $(T2 This, Dummy structure that is used to construct self-referencing algebraic types. Example: `Variant!(int, double, string, This*[2])`) 54 $(T2plain $(LREF SetAlias)`!setId`, Dummy structure that is used to construct cyclic-referencing lists of algebraic types. ) 55 $(T2 TaggedType, Dummy type used to associate tags with type. ) 56 ) 57 58 $(BOOKTABLE $(H3 $(LREF Algebraic) Traits), 59 $(TR $(TH Name) $(TH Description)) 60 $(T2 isVariant, Checks if the type is instance of $(LREF Algebraic).) 61 $(T2 isNullable, Checks if the type is instance of $(LREF Algebraic) with a self $(LREF TypeSet) that contains `typeof(null)`. ) 62 $(T2 isTaggedVariant, Checks if the type is instance of tagged $(LREF Algebraic).) 63 $(T2 isTypeSet, Checks if the types are the same as $(LREF TypeSet) of them. ) 64 $(T2 ValueTypeOfNullable, Gets type of $(LI $(LREF .Algebraic.get.2)) method. ) 65 66 ) 67 68 69 $(H3 Type Set) 70 $(UL 71 $(LI $(LREF TaggedTypeSet) is supported. Example:`TargetTypeSet!(["integer", "floating"], int, double)`). 72 $(LI Type set is unordered. Example:`TypeSet!(int, double)` and `TypeSet!(double, int)` are the same. ) 73 $(LI Duplicats are ignored. Example: `TypeSet!(float, int, float)` and `TypeSet!(int, float)` are the same. ) 74 $(LI Types are automatically unqualified if this operation can be performed implicitly. Example: `TypeSet!(const int) and `TypeSet!int` are the same. ) 75 $(LI Non trivial `TypeSet!(A, B, ..., etc)` is allowed.) 76 $(LI Trivial `TypeSet!T` is allowed.) 77 $(LI Empty `TypeSet!()` is allowed.) 78 ) 79 80 $(H3 Visitors) 81 $(UL 82 $(LI Visitors are allowed to return values of different types If there are more then one return type then the an $(LREF Algebraic) type is returned. ) 83 $(LI Visitors are allowed to accept additional arguments. The arguments can be passed to the visitor handler. ) 84 $(LI Multiple visitors can be passes to the visitor handler. ) 85 $(LI Visitors are matched according to the common $(HTTPS dlang.org/spec/function.html#function-overloading, Dlang Function Overloading) rules. ) 86 $(LI Visitors are allowed accept algebraic value by reference except the value of `typeof(null)`. ) 87 $(LI Visitors are called without algebraic value if its algebraic type is `void`. ) 88 $(LI If the visitors arguments has known types, then such visitors should be passed to a visitor handler before others to make the compiler happy. This includes visitors with no arguments, which is used to match `void` type. ) 89 ) 90 91 $(H3 Implementation Features) 92 $(UL 93 $(LI BetterC support. Runtime `TypeInfo` is not used.) 94 $(LI Copy-constructors and postblit constructors are supported. ) 95 $(LI `toHash`, `opCmp`. `opEquals`, and `toString` support. ) 96 $(LI No string or template mixins are used. ) 97 $(LI Optimised for fast execution. ) 98 ) 99 100 See_also: $(HTTPS en.wikipedia.org/wiki/Algebra_of_sets, Algebra of sets). 101 102 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 103 Authors: Ilya Yaroshenko 104 105 Macros: 106 T2plain=$(TR $(TDNW $1) $(TD $+)) 107 T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 108 T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4)) 109 T8=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4) $(TD $5) $(TD $6) $(TD $7) $(TD $8)) 110 111 +/ 112 module mir.algebraic; 113 114 import mir.internal.meta; 115 import mir.functional: naryFun; 116 117 private static immutable variantExceptionMsg = "mir.algebraic: the algebraic stores other type then requested."; 118 private static immutable variantNullExceptionMsg = "mir.algebraic: the algebraic is empty and doesn't store any value."; 119 private static immutable variantMemberExceptionMsg = "mir.algebraic: the algebraic stores a type that isn't compatible with the user provided visitor and arguments."; 120 121 version (D_Exceptions) 122 { 123 private static immutable variantException = new Exception(variantExceptionMsg); 124 private static immutable variantNullException = new Exception(variantNullExceptionMsg); 125 private static immutable variantMemberException = new Exception(variantMemberExceptionMsg); 126 } 127 128 private static struct _Null() 129 { 130 @safe pure nothrow @nogc const: 131 int opCmp(_Null) { return 0; } 132 this(typeof(null)) inout {} 133 string toString() { return "null"; } 134 } 135 136 private static struct _Void() 137 { 138 @safe pure nothrow @nogc const: 139 int opCmp(_Void) { return 0; } 140 string toString() { return "void"; } 141 } 142 143 /++ 144 Checks if the type is instance of $(LREF Algebraic). 145 +/ 146 enum bool isVariant(T) = is(T == Algebraic!Types, Types...); 147 148 /// 149 @safe pure version(mir_core_test) unittest 150 { 151 static assert(isVariant!(Variant!(int, string))); 152 static assert(isVariant!(const Variant!(int[], string))); 153 static assert(isVariant!(Nullable!(int, string))); 154 static assert(!isVariant!int); 155 } 156 157 /++ 158 Checks if the type is instance of tagged $(LREF Algebraic). 159 160 Tagged algebraics can be defined with $(LREF TaggedVariant). 161 +/ 162 enum bool isTaggedVariant(T) = isVariant!T && is(T.Kind == enum); 163 164 /// 165 @safe pure version(mir_core_test) unittest 166 { 167 static assert(!isTaggedVariant!int); 168 static assert(!isTaggedVariant!(Variant!(int, string))); 169 static assert(isTaggedVariant!(TaggedVariant!(["integer", "string"], int, string))); 170 } 171 172 /++ 173 Checks if the type is instance of $(LREF Algebraic) with a self $(LREF TypeSet) that contains `typeof(null)`. 174 +/ 175 enum bool isNullable(T) = is(T == Algebraic!(typeof(null), Types), Types...); 176 177 /// 178 @safe pure version(mir_core_test) unittest 179 { 180 static assert(isNullable!(const Nullable!(int, string))); 181 static assert(isNullable!(Nullable!())); 182 183 static assert(!isNullable!(Variant!())); 184 static assert(!isNullable!(Variant!string)); 185 static assert(!isNullable!int); 186 static assert(!isNullable!string); 187 } 188 189 /++ 190 Gets type of $(LI $(LREF .Algebraic.get.2)) method. 191 +/ 192 template ValueTypeOfNullable(T : Algebraic!(typeof(null), Types), Types...) 193 { 194 static if (Types.length == 1) 195 alias ValueTypeOfNullable = Types[0]; 196 else 197 alias ValueTypeOfNullable = Algebraic!Types; 198 } 199 200 /// 201 @safe pure version(mir_core_test) unittest 202 { 203 static assert(is(ValueTypeOfNullable!(const Nullable!(int, string)) == Algebraic!(int, string))); 204 static assert(is(ValueTypeOfNullable!(Nullable!()) == Algebraic!())); 205 static assert(is(typeof(Nullable!().get()) == Algebraic!())); 206 } 207 208 /++ 209 Dummy type for $(LREF Variant) and $(LREF Nullable) self-referencing. 210 +/ 211 struct This 212 { 213 @safe pure nothrow @nogc const: 214 int opCmp(typeof(this)) { return 0; } 215 string toString() { return typeof(this).stringof; } 216 } 217 218 /++ 219 Dummy type used to associate tags with a type. 220 +/ 221 struct TaggedType(T, string name) 222 if (name.length) 223 { 224 private enum tag = name; 225 private alias Type = T; 226 static if (!is(T == void) && !is(T == typeof(null))) 227 private T payload; 228 @safe pure nothrow @nogc const: 229 int opCmp(ref typeof(this)) { return 0; } 230 string toString() { return typeof(this).stringof; } 231 } 232 233 /++ 234 Checks if `T` is $(LREF TaggedType) instance. 235 +/ 236 enum isTaggedType(T) = is(T == TaggedType!(I, name), I, string name); 237 238 /++ 239 Gets $(LREF TaggedType) underlying type. 240 +/ 241 alias getTaggedTypeUnderlying(T : TaggedType!(I, name), I, string name) = I; 242 243 /++ 244 Gets $(LREF TaggedType) tag name. 245 +/ 246 enum getTaggedTypeName(T : TaggedType!(I, name), I, string name) = name; 247 248 // example from std.variant 249 /++ 250 $(H4 Self-Referential Types) 251 A useful and popular use of algebraic data structures is for defining 252 $(LUCKY self-referential data structures), i.e. structures that embed references to 253 values of their own type within. 254 This is achieved with $(LREF Variant) by using $(LREF This) as a placeholder whenever a 255 reference to the type being defined is needed. The $(LREF Variant) instantiation 256 will perform 257 $(LINK2 https://en.wikipedia.org/wiki/Name_resolution_(programming_languages)#Alpha_renaming_to_make_name_resolution_trivial, 258 alpha renaming) on its constituent types, replacing $(LREF This) 259 with the self-referenced type. The structure of the type involving $(LREF This) may 260 be arbitrarily complex. 261 +/ 262 @safe pure version(mir_core_test) unittest 263 { 264 import mir.functional: Tuple = RefTuple; 265 266 // A tree is either a leaf or a branch of two others 267 alias Tree(Leaf) = Variant!(Leaf, Tuple!(This*, This*)); 268 alias Leafs = Tuple!(Tree!int*, Tree!int*); 269 270 Tree!int tree = Leafs(new Tree!int(41), new Tree!int(43)); 271 Tree!int* right = tree.get!Leafs[1]; 272 assert(*right == 43); 273 } 274 275 /// 276 @safe pure version(mir_core_test) unittest 277 { 278 // An object is a double, a string, or a hash of objects 279 alias Obj = Variant!(double, string, This[string], This[]); 280 alias Map = Obj[string]; 281 282 Obj obj = "hello"; 283 assert(obj._is!string); 284 assert(obj.trustedGet!string == "hello"); 285 obj = 42.0; 286 assert(obj.get!double == 42); 287 obj = ["customer": Obj("John"), "paid": Obj(23.95)]; 288 assert(obj.get!Map["customer"] == "John"); 289 } 290 291 version(mir_core_test) unittest 292 { 293 static struct Foo 294 { 295 ~this() @system 296 { 297 } 298 } 299 alias TFoo = TaggedType!(Foo, "foo"); 300 TFoo foo; 301 } 302 303 304 /++ 305 Type set resolution template used to construct $(LREF Algebraic) . 306 +/ 307 template TypeSet(T...) 308 { 309 import std.meta: staticSort, staticMap, allSatisfy, anySatisfy; 310 // sort types by sizeof and them mangleof 311 // but typeof(null) goes first 312 static if (is(staticMap!(TryRemoveConst, T) == T)) 313 static if (is(NoDuplicates!T == T)) 314 static if (staticIsSorted!(TypeCmp, T)) 315 { 316 static if (anySatisfy!(isTaggedType, T)) 317 { 318 import std.meta: Filter, templateNot; 319 static assert(Filter!(templateNot!isTaggedType, T).length == 0, 320 "Either all or none types must be tagged. Types that doesn't have tags: " ~ 321 Filter!(templateNot!isTaggedType, T).stringof); 322 } 323 alias TypeSet = T; 324 } 325 else 326 alias TypeSet = .TypeSet!(staticSort!(TypeCmp, T)); 327 else 328 alias TypeSet = TypeSet!(NoDuplicates!T); 329 else 330 alias TypeSet = TypeSet!(staticMap!(TryRemoveConst, T)); 331 } 332 333 private template TypeCmp(A, B) 334 { 335 enum bool TypeCmp = is(A == B) ? false: 336 is(A == typeof(null)) || is(A == TaggedType!(typeof(null), aname), string aname) ? true: 337 is(B == typeof(null)) || is(B == TaggedType!(typeof(null), bname), string bname) ? false: 338 is(A == void) || is(A == TaggedType!(void, aname), string aname) ? true: 339 is(B == void) || is(A == TaggedType!(void, bname), string bname) ? false: 340 A.sizeof < B.sizeof ? true: 341 A.sizeof > B.sizeof ? false: 342 A.mangleof < B.mangleof; 343 } 344 345 /// 346 version(mir_core_test) unittest 347 { 348 struct S {} 349 alias C = S; 350 alias Int = int; 351 static assert(is(TypeSet!(S, int) == TypeSet!(Int, C))); 352 static assert(is(TypeSet!(S, int, int) == TypeSet!(Int, C))); 353 static assert(!is(TypeSet!(uint, S) == TypeSet!(int, S))); 354 } 355 356 private template applyTags(string[] tagNames, T...) 357 if (tagNames.length == T.length) 358 { 359 import std.meta: AliasSeq; 360 static if (tagNames.length == 0) 361 alias applyTags = AliasSeq!(); 362 else 363 alias applyTags = AliasSeq!(TaggedType!(T[0], tagNames[0]), .applyTags!(tagNames[1 .. $], T[1 .. $])); 364 } 365 366 /++ 367 Type set for tagged $(LREF Variants) self-referencing. 368 +/ 369 alias TaggedTypeSet(string[] tagNames, T...) = TypeSet!(applyTags!(tagNames, T)); 370 371 /++ 372 Checks if the type list is $(LREF TypeSet). 373 +/ 374 enum bool isTypeSet(T...) = is(T == TypeSet!T); 375 376 /// 377 @safe pure version(mir_core_test) unittest 378 { 379 static assert(isTypeSet!(TypeSet!())); 380 static assert(isTypeSet!(TypeSet!void)); 381 static assert(isTypeSet!(TypeSet!(void, int, typeof(null)))); 382 } 383 384 /++ 385 Variant Type (aka Algebraic Type). 386 387 The impllementation is defined as 388 ---- 389 alias Variant(T...) = Algebraic!(TypeSet!T); 390 ---- 391 392 Compatible with BetterC mode. 393 +/ 394 alias Variant(T...) = Algebraic!(TypeSet!T); 395 396 /// 397 @safe pure @nogc 398 version(mir_core_test) unittest 399 { 400 Variant!(int, double, string) v = 5; 401 assert(v.get!int == 5); 402 v = 3.14; 403 assert(v == 3.14); 404 // auto x = v.get!long; // won't compile, type long not allowed 405 // v = '1'; // won't compile, type char not allowed 406 } 407 408 /// Single argument Variant 409 // and Type with copy constructor 410 @safe pure nothrow @nogc 411 version(mir_core_test) unittest 412 { 413 static struct S 414 { 415 int n; 416 this(ref return scope inout S rhs) inout 417 { 418 this.n = rhs.n + 1; 419 } 420 } 421 422 Variant!S a = S(); 423 auto b = a; 424 425 import mir.conv; 426 assert(a.get!S.n == 0); 427 assert(b.n == 1); //direct access of a member in case of all algebraic types has this member 428 } 429 430 /// Empty type set 431 @safe pure nothrow @nogc version(mir_core_test) unittest 432 { 433 Variant!() a; 434 auto b = a; 435 assert(a.toHash == 0); 436 assert(a == b); 437 assert(a <= b && b >= a); 438 static assert(typeof(a).sizeof == 1); 439 } 440 441 /// Small types 442 @safe pure nothrow @nogc version(mir_core_test) unittest 443 { 444 struct S { ubyte d; } 445 static assert(Nullable!(byte, char, S).sizeof == 2); 446 } 447 448 @safe pure nothrow @nogc version(mir_core_test) unittest 449 { 450 struct S { ubyte[3] d; } 451 static assert(Nullable!(ushort, wchar, S).sizeof == 6); 452 } 453 454 // /// opPostMove support 455 // @safe pure @nogc nothrow 456 // version(mir_core_test) unittest 457 // { 458 // import std.algorithm.mutation: move; 459 460 // static struct S 461 // { 462 // uint s; 463 464 // void opPostMove(const ref S old) nothrow 465 // { 466 // this.s = old.s + 1; 467 // } 468 // } 469 470 // Variant!S a; 471 472 // auto b = a.move; 473 // assert(b.s == 1); 474 // } 475 476 /++ 477 Tagged Variant Type (aka Tagged Algebraic Type). 478 479 Compatible with BetterC mode. 480 481 Template has two declarations: 482 ---- 483 alias TaggedVariant(string[] tags, T...) = Variant!(applyTags!(tags, T)); 484 // and 485 template TaggedVariant(T) 486 if (is(T == union)) 487 { 488 ... 489 } 490 ---- 491 492 See_also: $(LREF Variant), $(LREF isTaggedVariant). 493 +/ 494 alias TaggedVariant(string[] tags, T...) = Variant!(applyTags!(tags, T)); 495 496 /// ditto 497 template TaggedVariant(T) 498 if (is(T == union)) 499 { 500 import std.meta: staticMap; 501 enum names = __traits(allMembers, T); 502 alias TypeOf(string member) = typeof(__traits(getMember, T, member)); 503 alias Types = staticMap!(TypeOf, names); 504 alias TaggedVariant = .TaggedVariant!([names], Types); 505 } 506 507 /// Json Value 508 @safe pure 509 version(mir_core_test) unittest 510 { 511 static union JsonUnion 512 { 513 long integer; 514 double floating; 515 bool boolean; 516 typeof(null) null_; 517 string string_; 518 This[] array; 519 This[string] object; 520 } 521 522 alias JsonValue = TaggedVariant!JsonUnion; 523 524 // typeof(null) has priority 525 static assert(JsonValue.Kind.init == JsonValue.Kind.null_); 526 static assert(JsonValue.Kind.null_ == 0); 527 528 // Kind and AllowedTypes has the same order 529 static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.array] == JsonValue[])); 530 static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.boolean] == bool)); 531 static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.floating] == double)); 532 static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.integer] == long)); 533 static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.null_] == typeof(null))); 534 static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.object] == JsonValue[string])); 535 536 JsonValue v; 537 assert(v.kind == JsonValue.Kind.null_); 538 539 v = 1; 540 assert(v.kind == JsonValue.Kind.integer); 541 assert(v == 1); 542 v = JsonValue(1); 543 assert(v == 1); 544 v = v.get!(long, double); 545 546 v = "Tagged!"; 547 assert(v.get !string == "Tagged!"); 548 assert(v.trustedGet!string == "Tagged!"); 549 550 assert(v.kind == JsonValue.Kind.string_); 551 552 assert(v.get !(JsonValue.Kind.string_) == "Tagged!"); // Kind-based get 553 assert(v.trustedGet!(JsonValue.Kind.string_) == "Tagged!"); // Kind-based trustedGet 554 555 v = [JsonValue("str"), JsonValue(4.3)]; 556 557 assert(v.kind == JsonValue.Kind.array); 558 assert(v.trustedGet!(JsonValue[])[1].kind == JsonValue.Kind.floating); 559 560 v = null; 561 assert(v.kind == JsonValue.Kind.null_); 562 } 563 564 /// Wrapped algebraic with propogated primitives 565 @safe pure 566 version(mir_core_test) unittest 567 { 568 static struct Response 569 { 570 alias Union = TaggedVariant!( 571 ["double_", "string", "array", "table"], 572 double, 573 string, 574 Response[], 575 Response[string], 576 ); 577 578 Union data; 579 alias Tag = Union.Kind; 580 // propogates opEquals, opAssign, and other primitives 581 alias data this; 582 583 static foreach (T; Union.AllowedTypes) 584 this(T v) @safe pure nothrow @nogc { data = v; } 585 } 586 587 Response v = 3.0; 588 assert(v.kind == Response.Tag.double_); 589 v = "str"; 590 assert(v == "str"); 591 } 592 593 /++ 594 Nullable $(LREF Variant) Type (aka Algebraic Type). 595 596 The impllementation is defined as 597 ---- 598 alias Nullable(T...) = Variant!(typeof(null), T); 599 ---- 600 601 In additional to common algebraic API the following members can be accesssed: 602 $(UL 603 $(LI $(LREF .Algebraic.isNull)) 604 $(LI $(LREF .Algebraic.nullify)) 605 $(LI $(LREF .Algebraic.get.2)) 606 ) 607 608 Compatible with BetterC mode. 609 +/ 610 alias Nullable(T...) = Variant!(typeof(null), T); 611 612 /// ditto 613 Nullable!T nullable(T)(T t) 614 { 615 import core.lifetime: forward; 616 return Nullable!T(forward!t); 617 } 618 619 /++ 620 Single type `Nullable` 621 +/ 622 @safe pure @nogc 623 version(mir_core_test) unittest 624 { 625 static assert(is(Nullable!int == Variant!(typeof(null), int))); 626 627 Nullable!int a = 5; 628 assert(a.get!int == 5); 629 630 a.nullify; 631 assert(a.isNull); 632 633 a = 4; 634 assert(!a.isNull); 635 assert(a.get == 4); 636 assert(a == 4); 637 a = 4; 638 639 a = null; 640 assert(a == null); 641 } 642 643 /// Empty nullable type set support 644 @safe pure nothrow @nogc version(mir_core_test) unittest 645 { 646 Nullable!() a; 647 auto b = a; 648 assert(a.toHash == 0); 649 assert(a == b); 650 assert(a <= b && b >= a); 651 static assert(typeof(a).sizeof == 1); 652 } 653 654 /++ 655 Algebraic implementation. 656 For more portable code, it is higly recommeded to don't use this template directly. 657 Instead, please use of $(LREF Variant) and $(LREF Nullable), which sort types. 658 +/ 659 struct Algebraic(_Types...) 660 { 661 import core.lifetime: moveEmplace; 662 import mir.conv: emplaceRef; 663 import mir.reflection: isPublic, hasField, isProperty; 664 import std.meta: Filter, AliasSeq, ApplyRight, anySatisfy, allSatisfy, staticMap, templateOr, templateNot; 665 import std.traits: 666 hasElaborateAssign, 667 hasElaborateCopyConstructor, 668 hasElaborateDestructor, 669 hasMember, 670 isEqualityComparable, 671 isOrderingComparable, 672 Largest, 673 Unqual 674 ; 675 676 private enum bool _variant_test_ = is(_Types == AliasSeq!(typeof(null), double)); 677 678 static if (anySatisfy!(isTaggedType, _Types)) 679 { 680 private alias _UntaggedThisTypeSetList = staticMap!(getTaggedTypeUnderlying, _Types); 681 } 682 else 683 { 684 private alias _UntaggedThisTypeSetList = _Types; 685 } 686 687 /++ 688 Allowed types list 689 See_also: $(LREF TypeSet) 690 +/ 691 alias AllowedTypes = AliasSeq!(ReplaceTypeUnless!(isVariant, This, Algebraic!_Types, _UntaggedThisTypeSetList)); 692 693 version(mir_core_test) 694 static if (_variant_test_) 695 /// 696 unittest 697 { 698 import std.meta: AliasSeq; 699 700 alias V = Nullable! 701 ( 702 This*, 703 string, 704 double, 705 bool, 706 ); 707 708 static assert(is(V.AllowedTypes == TypeSet!( 709 typeof(null), 710 bool, 711 string, 712 double, 713 V*))); 714 } 715 716 private alias _Payload = Replace!(void, _Void!(), Replace!(typeof(null), _Null!(), AllowedTypes)); 717 718 private static union _Storage_ 719 { 720 _Payload payload; 721 722 static foreach (int i, P; _Payload) 723 mixin(`alias _member_` ~ i.stringof ~ ` = payload[` ~ i.stringof ~ `];`); 724 725 static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null)))) 726 ubyte[0] bytes; 727 else 728 ubyte[Largest!_Payload.sizeof] bytes; 729 } 730 731 private _Storage_ _storage_; 732 733 static if (AllowedTypes.length > 1) 734 { 735 static if ((_Storage_.alignof & 1) && _Payload.length <= ubyte.max) 736 private alias _ID_ = ubyte; 737 else 738 static if ((_Storage_.alignof & 2) && _Payload.length <= ushort.max) 739 private alias _ID_ = ushort; 740 else 741 static if (_Storage_.alignof & 3) 742 private alias _ID_ = uint; 743 else 744 private alias _ID_ = ulong; 745 746 _ID_ _identifier_; 747 } 748 else 749 { 750 alias _ID_ = uint; 751 enum _ID_ _identifier_ = 0; 752 } 753 754 version (D_Ddoc) 755 { 756 /++ 757 Algebraic Kind. 758 759 Defined as enum for tagged algebraics and as unsigned for common algebraics. 760 761 The Kind enum contains the members defined using tag names. 762 763 If the algebraic type is $(LREF Nullable) then the default Kind enum member has zero value and corresponds to `typeof(null)`. 764 765 See_also: $(LREF TaggedVariant). 766 +/ 767 enum Kind { _not_me_but_tags_name_list_ } 768 } 769 770 static if (anySatisfy!(isTaggedType, _Types)) 771 { 772 version (D_Ddoc){} 773 else 774 { 775 mixin(enumKindText([staticMap!(getTaggedTypeName, _Types)])); 776 777 } 778 } 779 else 780 { 781 version (D_Ddoc){} 782 else 783 { 784 alias Kind = _ID_; 785 } 786 } 787 788 /++ 789 Returns: $(LREF .Algebraic.Kind). 790 791 Defined as enum for tagged algebraics and as unsigned for common algebraics. 792 See_also: $(LREF TaggedVariant). 793 +/ 794 Kind kind() const @safe pure nothrow @nogc @property 795 { 796 assert(_identifier_ <= Kind.max); 797 return cast(Kind) _identifier_; 798 } 799 800 static if (anySatisfy!(hasElaborateDestructor, _Payload)) 801 ~this() @trusted 802 { 803 S: switch (_identifier_) 804 { 805 static foreach (i, T; AllowedTypes) 806 static if (hasElaborateDestructor!T) 807 { 808 case i: 809 (*cast(Unqual!(_Payload[i])*)&_storage_.payload[i]).__xdtor; 810 break S; 811 } 812 default: 813 } 814 version(mir_secure_memory) 815 _storage_.bytes = 0xCC; 816 } 817 818 // static if (anySatisfy!(hasOpPostMove, _Payload)) 819 // void opPostMove(const ref typeof(this) old) 820 // { 821 // S: switch (_identifier_) 822 // { 823 // static foreach (i, T; AllowedTypes) 824 // static if (hasOpPostMove!T) 825 // { 826 // case i: 827 // this._storage_.payload[i].opPostMove(old._storage_.payload[i]); 828 // return; 829 // } 830 // default: return; 831 // } 832 // } 833 834 static if (AllowedTypes.length) 835 { 836 static if (!__traits(compiles, (){ _Payload[0] arg; })) 837 { 838 @disable this(); 839 } 840 } 841 842 /// Construct an algebraic type from its subset. 843 this(RhsTypes...)(Algebraic!RhsTypes rhs) 844 if (allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes)) 845 { 846 import core.lifetime: move; 847 static if (is(RhsTypes == _Types)) 848 this = move(rhs); 849 else 850 { 851 switch (rhs._identifier_) 852 { 853 static foreach (i, T; Algebraic!RhsTypes.AllowedTypes) 854 { 855 case i: 856 static if (__traits(compiles, __ctor(move(rhs.trustedGet!T)))) 857 __ctor(move(rhs.trustedGet!T)); 858 else 859 __ctor(rhs.trustedGet!T); 860 return; 861 } 862 default: 863 assert(0, variantMemberExceptionMsg); 864 } 865 } 866 } 867 868 version(mir_core_test) 869 static if (_variant_test_) 870 /// 871 unittest 872 { 873 alias Float = Variant!(float, double); 874 alias Int = Variant!(long, int); 875 alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes); 876 877 Float fp = 3.0; 878 Number number = fp; // constructor call 879 assert(number == 3.0); 880 881 Int integer = 12L; 882 number = Number(integer); 883 assert(number == 12L); 884 } 885 886 static if (!allSatisfy!(isCopyable, AllowedTypes)) 887 @disable this(this); 888 else 889 static if (anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) 890 { 891 // private enum _allCanImplicitlyRemoveConst = allSatisfy!(canImplicitlyRemoveConst, AllowedTypes); 892 // private enum _allCanRemoveConst = allSatisfy!(canRemoveConst, AllowedTypes); 893 // private enum _allHaveImplicitSemiMutableConstruction = _allCanImplicitlyRemoveConst && _allHaveMutableConstruction; 894 895 static if (__VERSION__ < 2094) 896 private static union _StorageI(uint i) 897 { 898 _Payload[i] payload; 899 ubyte[_Storage_.bytes.length] bytes; 900 } 901 902 static if (allSatisfy!(hasInoutConstruction, AllowedTypes)) 903 this(return ref scope inout Algebraic rhs) inout 904 { 905 static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_; 906 static foreach (int i, T; AllowedTypes) 907 static if (!is(T == typeof(null)) && !is(T == void)) 908 { 909 if (_identifier_ == i) 910 { 911 static if (__VERSION__ < 2094) 912 { 913 _storage_.bytes = () inout @trusted { 914 auto ret = inout _StorageI!i(rhs.trustedGet!T); 915 return ret.bytes; 916 } (); 917 return; 918 } 919 else 920 { 921 _storage_ = () inout { 922 mixin(`inout _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); 923 return ret; 924 } (); 925 return; 926 } 927 } 928 } 929 } 930 else 931 { 932 static if (allSatisfy!(hasMutableConstruction, AllowedTypes)) 933 this(return ref scope Algebraic rhs) 934 { 935 static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_; 936 static foreach (int i, T; AllowedTypes) 937 static if (!is(T == typeof(null)) && !is(T == void)) 938 { 939 if (_identifier_ == i) 940 { 941 _storage_ = () { 942 mixin(`_Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); 943 return ret; 944 } (); 945 return; 946 } 947 } 948 } 949 950 static if (allSatisfy!(hasConstConstruction, AllowedTypes)) 951 this(return ref scope const Algebraic rhs) const 952 { 953 static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_; 954 static foreach (int i, T; AllowedTypes) 955 static if (!is(T == typeof(null)) && !is(T == void)) 956 { 957 if (_identifier_ == i) 958 { 959 _storage_ = () const { 960 mixin(`const _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); 961 return ret; 962 } (); 963 return; 964 } 965 } 966 } 967 968 static if (allSatisfy!(hasImmutableConstruction, AllowedTypes)) 969 this(return ref scope immutable Algebraic rhs) immutable 970 { 971 static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_; 972 static foreach (int i, T; AllowedTypes) 973 static if (!is(T == typeof(null)) && !is(T == void)) 974 { 975 if (_identifier_ == i) 976 { 977 _storage_ = () immutable { 978 mixin(`immutable _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); 979 return ret; 980 } (); 981 return; 982 } 983 } 984 } 985 986 static if (allSatisfy!(hasSemiImmutableConstruction, AllowedTypes)) 987 this(return ref scope const Algebraic rhs) immutable 988 { 989 static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_; 990 static foreach (int i, T; AllowedTypes) 991 static if (!is(T == typeof(null)) && !is(T == void)) 992 { 993 if (_identifier_ == i) 994 { 995 _storage_ = () const { 996 mixin(`immutable _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); 997 return ret; 998 } (); 999 return; 1000 } 1001 } 1002 } 1003 1004 static if (allSatisfy!(hasSemiMutableConstruction, AllowedTypes)) 1005 this(return ref scope const Algebraic rhs) 1006 { 1007 static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_; 1008 static foreach (int i, T; AllowedTypes) 1009 static if (!is(T == typeof(null)) && !is(T == void)) 1010 { 1011 if (_identifier_ == i) 1012 { 1013 _storage_ = () const { 1014 mixin(`const _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); 1015 return ret; 1016 } (); 1017 return; 1018 } 1019 } 1020 } 1021 } 1022 } 1023 1024 /++ 1025 +/ 1026 size_t toHash() @trusted nothrow const 1027 { 1028 static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null)))) 1029 { 1030 return 0; 1031 } 1032 else 1033 switch (_identifier_) 1034 { 1035 static foreach (i, T; AllowedTypes) 1036 { 1037 case i: 1038 static if (is(T == void)) 1039 return i; 1040 else 1041 static if (is(T == typeof(null))) 1042 return i; 1043 else 1044 static if (__traits(compiles, hashOf(trustedGet!T, cast(size_t)i))) 1045 return hashOf(trustedGet!T, cast(size_t)i); 1046 else 1047 { 1048 debug pragma(msg, "Mir warning: can't compute hash of " ~ (const T).stringof); 1049 return i; 1050 } 1051 } 1052 default: assert(0); 1053 } 1054 } 1055 1056 /++ 1057 +/ 1058 bool opEquals()(auto ref const typeof(this) rhs) const @trusted 1059 { 1060 static if (AllowedTypes.length == 0) 1061 { 1062 return true; 1063 } 1064 else 1065 { 1066 if (this._identifier_ != rhs._identifier_) 1067 return false; 1068 switch (_identifier_) 1069 { 1070 static foreach (i, T; AllowedTypes) 1071 { 1072 case i: 1073 return this.trustedGet!T == rhs.trustedGet!T; 1074 } 1075 default: assert(0); 1076 } 1077 } 1078 } 1079 1080 /++ 1081 +/ 1082 static if (is(AllowedTypes == _Types)) 1083 auto opCmp()(auto ref const typeof(this) rhs) const @trusted 1084 { 1085 static if (AllowedTypes.length == 0) 1086 { 1087 return 0; 1088 } 1089 else 1090 { 1091 import mir.internal.utility: isFloatingPoint; 1092 if (auto d = int(this._identifier_) - int(rhs._identifier_)) 1093 return d; 1094 switch (_identifier_) 1095 { 1096 static foreach (i, T; AllowedTypes) 1097 { 1098 case i: 1099 static if (__traits(compiles, __cmp(trustedGet!T, rhs.trustedGet!T))) 1100 return __cmp(trustedGet!T, rhs.trustedGet!T); 1101 else 1102 static if (__traits(hasMember, T, "opCmp") && !is(T == U*, U)) 1103 return this.trustedGet!T.opCmp(rhs.trustedGet!T); 1104 else 1105 // static if (isFloatingPoint!T) 1106 // return trustedGet!T == rhs ? 0 : trustedGet!T - rhs.trustedGet!T; 1107 // else 1108 return this.trustedGet!T < rhs.trustedGet!T ? -1 : 1109 this.trustedGet!T > rhs.trustedGet!T ? +1 : 0; 1110 } 1111 default: assert(0); 1112 } 1113 } 1114 } 1115 1116 /// Requires mir-algorithm package 1117 string toString()() const 1118 { 1119 static if (AllowedTypes.length == 0) 1120 { 1121 return "Algebraic"; 1122 } 1123 else 1124 { 1125 import mir.conv: to; 1126 switch (_identifier_) 1127 { 1128 static foreach (i, T; AllowedTypes) 1129 { 1130 case i: 1131 static if (is(T == void)) 1132 return "void"; 1133 else 1134 static if (is(T == typeof(null))) 1135 return "null"; 1136 else 1137 static if (__traits(compiles, { auto s = to!string(trustedGet!T);})) 1138 return to!string(trustedGet!T); 1139 else 1140 return AllowedTypes[i].stringof; 1141 } 1142 default: assert(0); 1143 } 1144 } 1145 } 1146 1147 ///ditto 1148 void toString(W)(scope ref W w) const 1149 { 1150 static if (AllowedTypes.length == 0) 1151 { 1152 return w.put("Algebraic"); 1153 } 1154 else 1155 { 1156 switch (_identifier_) 1157 { 1158 static foreach (i, T; AllowedTypes) 1159 { 1160 case i: 1161 static if (is(T == void)) 1162 return w.put("void"); 1163 else 1164 static if (is(T == typeof(null))) 1165 return w.put("null"); 1166 else 1167 static if (__traits(compiles, { import mir.format: print; print(w, trustedGet!T); })) 1168 { import mir.format: print; print(w, trustedGet!T); } 1169 else 1170 w.put(AllowedTypes[i].stringof); 1171 return; 1172 } 1173 default: assert(0); 1174 } 1175 } 1176 } 1177 1178 static if (is(AllowedTypes[0] == typeof(null))) 1179 { 1180 /// 1181 bool opCast(C)() const 1182 if (is(C == bool)) 1183 { 1184 return _identifier_ != 0; 1185 } 1186 1187 /// 1188 Algebraic opCast(C)() const 1189 if (is(C == Algebraic)) 1190 { 1191 return this; 1192 } 1193 1194 /// Defined if the first type is `typeof(null)` 1195 bool isNull() const @property { return _identifier_ == 0; } 1196 /// ditto 1197 void nullify() { this = null; } 1198 1199 /// ditto 1200 auto get()() 1201 if (allSatisfy!(isCopyable, AllowedTypes[1 .. $]) && AllowedTypes.length != 2) 1202 { 1203 import mir.utility: _expect; 1204 if (_expect(!_identifier_, false)) 1205 { 1206 throw variantNullException; 1207 } 1208 static if (AllowedTypes.length != 2) 1209 { 1210 Algebraic!(_Types[1 .. $]) ret; 1211 1212 S: switch (_identifier_) 1213 { 1214 static foreach (i, T; AllowedTypes[1 .. $]) 1215 { 1216 { 1217 case i + 1: 1218 if (!hasElaborateCopyConstructor!T && !__ctfe) 1219 goto default; 1220 ret = this.trustedGet!T; 1221 break S; 1222 } 1223 } 1224 default: 1225 ret._storage_.bytes = this._storage_.bytes; 1226 static if (ret.AllowedTypes.length > 1) 1227 ret._identifier_ = cast(typeof(ret._identifier_))(this._identifier_ - 1); 1228 } 1229 return ret; 1230 } 1231 } 1232 1233 static if (AllowedTypes.length == 2) 1234 { 1235 /++ 1236 Gets the value if not null. If `this` is in the null state, and the optional 1237 parameter `fallback` was provided, it will be returned. Without `fallback`, 1238 calling `get` with a null state is invalid. 1239 1240 When the fallback type is different from the Nullable type, `get(T)` returns 1241 the common type. 1242 1243 Params: 1244 fallback = the value to return in case the `Nullable` is null. 1245 1246 Returns: 1247 The value held internally by this `Nullable`. 1248 +/ 1249 auto ref inout(AllowedTypes[1]) get() return inout 1250 { 1251 assert(_identifier_, "Called `get' on null Nullable!(" ~ AllowedTypes[1].stringof ~ ")."); 1252 return trustedGet!(AllowedTypes[1]); 1253 } 1254 1255 version(mir_core_test) 1256 static if (_variant_test_) 1257 /// 1258 @safe pure nothrow @nogc 1259 unittest 1260 { 1261 enum E { a = "a", b = "b" } 1262 Nullable!E f = E.a; 1263 auto e = f.get(); 1264 static assert(is(typeof(e) == E), Nullable!E.AllowedTypes.stringof); 1265 assert(e == E.a); 1266 1267 assert(f.get(E.b) == E.a); 1268 1269 f = null; 1270 assert(f.get(E.b) == E.b); 1271 } 1272 1273 /// ditto 1274 @property auto ref inout(AllowedTypes[1]) get()(auto ref inout(AllowedTypes[1]) fallback) return inout 1275 { 1276 return isNull ? fallback : get(); 1277 } 1278 } 1279 } 1280 1281 /++ 1282 Checks if the underlaying type is an element of a user provided type set. 1283 +/ 1284 bool _is(R : Algebraic!RetTypes, RetTypes...)() @safe pure nothrow @nogc const @property 1285 if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes)) 1286 { 1287 static if (is(RetTypes == _Types)) 1288 return true; 1289 else 1290 { 1291 import std.meta: staticIndexOf; 1292 import std.traits: CopyTypeQualifiers; 1293 alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes; 1294 alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes); 1295 // uint rhsTypeId; 1296 switch (_identifier_) 1297 { 1298 foreach (i, T; AllowedTypes) 1299 static if (staticIndexOf!(T, RhsAllowedTypes) >= 0) 1300 { 1301 case i: 1302 return true; 1303 } 1304 default: 1305 return false; 1306 } 1307 } 1308 } 1309 1310 /// ditto 1311 bool _is(RetTypes...)() @safe pure nothrow @nogc const @property 1312 if (RetTypes.length > 1) 1313 { 1314 return this._is!(Variant!RetTypes); 1315 } 1316 1317 /++ 1318 `nothrow` $(LREF .Algebraic.get) alternative that returns an algebraic subset. 1319 +/ 1320 auto ref trustedGet(R : Algebraic!RetTypes, this This, RetTypes...)() return @property 1321 if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes)) 1322 { 1323 static if (is(RetTypes == _Types)) 1324 return this; 1325 else 1326 { 1327 import std.meta: staticIndexOf; 1328 import std.traits: CopyTypeQualifiers; 1329 alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes; 1330 alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes); 1331 // uint rhsTypeId; 1332 switch (_identifier_) 1333 { 1334 foreach (i, T; AllowedTypes) 1335 static if (staticIndexOf!(T, RhsAllowedTypes) >= 0) 1336 { 1337 case i: 1338 static if (is(T == void)) 1339 return (()@trusted => cast(Ret) Ret._void)(); 1340 else 1341 return Ret(trustedGet!T); 1342 } 1343 default: 1344 assert(0, variantMemberExceptionMsg); 1345 } 1346 } 1347 } 1348 1349 /// ditto 1350 template trustedGet(RetTypes...) 1351 if (RetTypes.length > 1) 1352 { 1353 /// 1354 auto ref trustedGet(this This)() return 1355 { 1356 return this.trustedGet!(Variant!RetTypes); 1357 } 1358 } 1359 1360 version(mir_core_test) 1361 static if (_variant_test_) 1362 /// 1363 @safe pure nothrow @nogc 1364 unittest 1365 { 1366 alias Float = Variant!(float, double); 1367 alias Int = Variant!(long, int); 1368 alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes); 1369 1370 Number number = 3.0; 1371 assert(number._is!Float); 1372 auto fp = number.trustedGet!Float; 1373 static assert(is(typeof(fp) == Float)); 1374 assert(fp == 3.0); 1375 1376 // type list overload 1377 number = 12L; 1378 assert(number._is!(int, long)); 1379 auto integer = number.trustedGet!(int, long); 1380 static assert(is(typeof(integer) == Int)); 1381 assert(integer == 12L); 1382 } 1383 1384 static if (anySatisfy!(isTaggedType, _Types)) 1385 /// `trustedGet` overload that accept $(LREF .Algebraic.Kind). 1386 alias trustedGet(Kind kind) = trustedGet!(AllowedTypes[kind]); 1387 1388 /++ 1389 Gets an algebraic subset. 1390 1391 Throws: Exception if the storage contains value of the type that isn't represented in the allowed type set of the requested algebraic. 1392 +/ 1393 auto ref get(R : Algebraic!RetTypes, this This, RetTypes...)() return @property 1394 if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes)) 1395 { 1396 static if (is(RetTypes == _Types)) 1397 return this; 1398 else 1399 { 1400 import std.meta: staticIndexOf; 1401 import std.traits: CopyTypeQualifiers; 1402 alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes; 1403 alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes); 1404 // uint rhsTypeId; 1405 switch (_identifier_) 1406 { 1407 foreach (i, T; AllowedTypes) 1408 static if (staticIndexOf!(T, RhsAllowedTypes) >= 0) 1409 { 1410 case i: 1411 static if (is(T == void)) 1412 return (()@trusted => cast(Ret) Ret._void)(); 1413 else 1414 return Ret(trustedGet!T); 1415 } 1416 default: 1417 throw variantMemberException; 1418 } 1419 } 1420 } 1421 1422 /// ditto 1423 template get(RetTypes...) 1424 if (RetTypes.length > 1) 1425 { 1426 /// 1427 auto ref get(this This)() return 1428 { 1429 return this.get!(Variant!RetTypes); 1430 } 1431 } 1432 1433 version(mir_core_test) 1434 static if (_variant_test_) 1435 /// 1436 @safe pure @nogc 1437 unittest 1438 { 1439 alias Float = Variant!(float, double); 1440 alias Int = Variant!(long, int); 1441 alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes); 1442 1443 Number number = 3.0; 1444 auto fp = number.get!Float; 1445 static assert(is(typeof(fp) == Float)); 1446 assert(fp == 3.0); 1447 1448 // type list overload 1449 number = 12L; 1450 auto integer = number.get!(int, long); 1451 static assert(is(typeof(integer) == Int)); 1452 assert(integer == 12L); 1453 } 1454 1455 static if (anySatisfy!(isTaggedType, _Types)) 1456 /// `get` overload that accept $(LREF .Algebraic.Kind). 1457 alias get(Kind kind) = get!(AllowedTypes[kind]); 1458 1459 private alias _ReflectionTypes = AllowedTypes[is(AllowedTypes[0] == typeof(null)) .. $]; 1460 1461 static if (_ReflectionTypes.length) 1462 this(this This, Args...)(auto ref Args args) 1463 if (Args.length && (Args.length > 1 || !isVariant!(Args[0]))) 1464 { 1465 import std.traits: CopyTypeQualifiers; 1466 import core.lifetime: forward; 1467 1468 template CanCompile(T) 1469 { 1470 alias Q = CopyTypeQualifiers!(This, T); 1471 enum CanCompile = __traits(compiles, new Q(forward!args)); 1472 } 1473 1474 alias TargetType = Filter!(CanCompile, _ReflectionTypes); 1475 static if (TargetType.length == 0) 1476 static assert(0, typeof(this).stringof ~ ".this: no types can be constructed with arguments " ~ Args.stringof); 1477 static assert(TargetType.length == 1, typeof(this).stringof ~ ".this: multiple types " ~ TargetType.stringof ~ " can be constructed with arguments " ~ Args.stringof); 1478 alias TT = TargetType[0]; 1479 static if (is(TT == struct) || is(TT == union)) 1480 this(CopyTypeQualifiers!(This, TT)(forward!args)); 1481 else 1482 this(new CopyTypeQualifiers!(This, TT)(forward!args)); 1483 } 1484 1485 static if (_ReflectionTypes.length && allSatisfy!(isSimpleAggregateType, _ReflectionTypes)) 1486 { 1487 static foreach (member; AllMembersRec!(_ReflectionTypes[0])) 1488 static if ( 1489 member != "_ID_" && 1490 member != "_identifier_" && 1491 member != "_is" && 1492 member != "_storage_" && 1493 member != "_Storage_" && 1494 member != "_variant_test_" && 1495 member != "_void" && 1496 member != "AllowedTypes" && 1497 member != "get" && 1498 member != "isNull" && 1499 member != "kind" && 1500 member != "Kind" && 1501 member != "nullify" && 1502 member != "opAssign" && 1503 member != "opCast" && 1504 member != "opCmp" && 1505 member != "opEquals" && 1506 member != "opPostMove" && 1507 member != "toHash" && 1508 member != "toString" && 1509 member != "trustedGet" && 1510 member != "deserializeFromAsdf" && 1511 member != "deserializeFromIon" && 1512 !(member.length >= 2 && member[0 .. 2] == "__")) 1513 static if (allSatisfy!(ApplyRight!(hasMember, member), _ReflectionTypes)) 1514 static if (!anySatisfy!(ApplyRight!(isMemberType, member), _ReflectionTypes)) 1515 static if (allSatisfy!(ApplyRight!(isSingleMember, member), _ReflectionTypes)) 1516 static if (allSatisfy!(ApplyRight!(isPublic, member), _ReflectionTypes)) 1517 { 1518 static if (allSatisfy!(ApplyRight!(hasField, member), _ReflectionTypes) && NoDuplicates!(staticMap!(ApplyRight!(memberTypeOf, member), _ReflectionTypes)).length == 1) 1519 { 1520 mixin(`ref ` ~ member ~q{()() inout return @trusted pure nothrow @nogc @property { return this.getMember!member; }}); 1521 } 1522 else 1523 static if (allSatisfy!(ApplyRight!(templateOr!(hasField, isProperty), member), _ReflectionTypes)) 1524 { 1525 mixin(`auto ref ` ~ member ~q{(this This, Args...)(auto ref Args args) @property { static if (args.length) { import core.lifetime: forward; return this.getMember!member = forward!args; } else return this.getMember!member; }}); 1526 } 1527 static if (allSatisfy!(ApplyRight!(templateNot!(templateOr!(hasField, isProperty)), member), _ReflectionTypes)) 1528 { 1529 mixin(`auto ref ` ~ member ~q{(this This, Args...)(auto ref Args args) { static if (args.length) { import core.lifetime: forward; return this.getMember!member(forward!args); } else return this.getMember!member; }}); 1530 } 1531 } 1532 } 1533 1534 /// 1535 ref opAssign(RhsTypes...)(Algebraic!RhsTypes rhs) return @trusted 1536 if (RhsTypes.length < AllowedTypes.length && allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes)) 1537 { 1538 import core.lifetime: forward; 1539 static if (anySatisfy!(hasElaborateDestructor, AllowedTypes)) 1540 this.__dtor(); 1541 __ctor(forward!rhs); 1542 return this; 1543 } 1544 1545 static foreach (int i, T; AllowedTypes) 1546 { 1547 /// Zero cost always nothrow `get` alternative 1548 auto ref trustedGet(E)() @trusted @property return inout nothrow 1549 if (is(E == T)) 1550 { 1551 assert (i == _identifier_); 1552 static if (is(T == typeof(null))) 1553 return null; 1554 else 1555 static if (is(T == void)) 1556 return; 1557 else 1558 return _storage_.payload[i]; 1559 } 1560 1561 /++ 1562 Throws: Exception if the storage contains value of other type 1563 +/ 1564 auto ref get(E)() @property return inout 1565 if (is(E == T)) 1566 { 1567 import mir.utility: _expect; 1568 static if (AllowedTypes.length > 1) 1569 { 1570 if (_expect(i != _identifier_, false)) 1571 { 1572 throw variantException; 1573 } 1574 } 1575 return trustedGet!T; 1576 } 1577 1578 /++ 1579 Checks if the storage stores an allowed type. 1580 +/ 1581 bool _is(E)() const @property nothrow @nogc 1582 if (is(E == T)) 1583 { 1584 return _identifier_ == i; 1585 } 1586 1587 static if (is(T == void)) 1588 { 1589 /// Defined if `AllowedTypes` contains `void` 1590 static Algebraic _void() 1591 { 1592 Algebraic ret; 1593 ret._storage_ = () { 1594 import core.lifetime: forward; 1595 mixin(`_Storage_ ret = { _member_` ~ i.stringof ~ ` : _Void!().init };`); 1596 return ret; 1597 } (); 1598 ret._identifier_ = i; 1599 return ret; 1600 } 1601 } 1602 else 1603 { 1604 /// 1605 static if (isCopyable!(const T) || is(Unqual!T == T)) 1606 this(T value) 1607 { 1608 import core.lifetime: forward; 1609 static if (is(T == typeof(null))) 1610 auto rhs = _Null!()(); 1611 else 1612 alias rhs = forward!value; 1613 1614 static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) 1615 { 1616 _storage_.bytes = () @trusted { 1617 auto ret = _StorageI!i(rhs); 1618 return ret.bytes; 1619 } (); 1620 } 1621 else 1622 { 1623 _storage_ = () { 1624 mixin(`_Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs };`); 1625 return ret; 1626 } (); 1627 } 1628 static if (_Payload.length > 1) 1629 _identifier_ = i; 1630 } 1631 1632 /// ditto 1633 static if (isCopyable!(const T)) 1634 this(const T value) const 1635 { 1636 static if (is(T == typeof(null))) 1637 auto rhs = _Null!()(); 1638 else 1639 alias rhs = value; 1640 static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) 1641 { 1642 _storage_.bytes = () const @trusted { 1643 auto ret = const _StorageI!i(rhs); 1644 return ret.bytes; 1645 } (); 1646 } 1647 else 1648 { 1649 _storage_ = () { 1650 mixin(`const _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs };`); 1651 return ret; 1652 } (); 1653 } 1654 static if (_Payload.length > 1) 1655 _identifier_ = i; 1656 } 1657 1658 /// ditto 1659 static if (isCopyable!(immutable T)) 1660 this(immutable T value) immutable 1661 { 1662 static if (is(T == typeof(null))) 1663 auto rhs = _Null!()(); 1664 else 1665 alias rhs = value; 1666 static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) 1667 { 1668 _storage_.bytes = () const @trusted { 1669 auto ret = immutable _StorageI!i(rhs); 1670 return ret.bytes; 1671 } (); 1672 } 1673 else 1674 { 1675 _storage_ = () { 1676 mixin(`immutable _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs };`); 1677 return ret; 1678 } (); 1679 } 1680 static if (_Payload.length > 1) 1681 _identifier_ = i; 1682 } 1683 1684 static if (__traits(compiles, (ref T a, ref T b) { moveEmplace(a, b); })) 1685 /// 1686 ref opAssign(T rhs) return @trusted 1687 { 1688 import core.lifetime: forward; 1689 static if (anySatisfy!(hasElaborateDestructor, AllowedTypes)) 1690 this.__dtor(); 1691 __ctor(forward!rhs); 1692 return this; 1693 } 1694 1695 /++ 1696 +/ 1697 auto opEquals()(auto ref const T rhs) const 1698 { 1699 static if (AllowedTypes.length > 1) 1700 if (_identifier_ != i) 1701 return false; 1702 return trustedGet!T == rhs; 1703 } 1704 1705 /++ 1706 +/ 1707 auto opCmp()(auto ref const T rhs) const 1708 { 1709 import mir.internal.utility: isFloatingPoint; 1710 static if (AllowedTypes.length > 1) 1711 if (auto d = int(_identifier_) - int(i)) 1712 return d; 1713 static if (__traits(compiles, __cmp(trustedGet!T, rhs))) 1714 return __cmp(trustedGet!T, rhs); 1715 else 1716 static if (__traits(hasMember, T, "opCmp") && !is(T == U*, U)) 1717 return trustedGet!T.opCmp(rhs); 1718 else 1719 static if (isFloatingPoint!T) 1720 return trustedGet!T == rhs ? 0 : trustedGet!T - rhs; 1721 else 1722 return trustedGet!T < rhs ? -1 : 1723 trustedGet!T > rhs ? +1 : 0; 1724 } 1725 1726 static if (is(Unqual!T == bool)) 1727 { 1728 private alias contains = Contains!AllowedTypes; 1729 static if (contains!long && !contains!int) 1730 { 1731 this(int value) 1732 { 1733 this(long(value)); 1734 } 1735 1736 this(int value) const 1737 { 1738 this(long(value)); 1739 } 1740 1741 this(int value) immutable 1742 { 1743 this(long(value)); 1744 } 1745 1746 ref opAssign(int rhs) return @trusted 1747 { 1748 return opAssign(long(rhs)); 1749 } 1750 1751 auto opEquals()(int rhs) const 1752 { 1753 return opEquals(long(rhs)); 1754 } 1755 1756 auto opCmp()(int rhs) const 1757 { 1758 return opCmp(long(rhs)); 1759 } 1760 } 1761 1762 static if (contains!ulong && !contains!uint) 1763 { 1764 this(uint value) 1765 { 1766 this(ulong(value)); 1767 } 1768 1769 this(uint value) const 1770 { 1771 this(ulong(value)); 1772 } 1773 1774 this(uint value) immutable 1775 { 1776 this(ulong(value)); 1777 } 1778 1779 ref opAssign(uint rhs) return @trusted 1780 { 1781 return opAssign(ulong(rhs)); 1782 } 1783 1784 auto opEquals()(uint rhs) const 1785 { 1786 return opEquals(ulong(rhs)); 1787 } 1788 1789 auto opCmp()(uint rhs) const 1790 { 1791 return opCmp(ulong(rhs)); 1792 } 1793 } 1794 } 1795 } 1796 } 1797 } 1798 1799 /++ 1800 Constructor and methods propagation. 1801 +/ 1802 version(mir_core_test) 1803 unittest 1804 { 1805 static struct Base 1806 { 1807 double d; 1808 } 1809 1810 static class C 1811 { 1812 // alias this members are supported 1813 Base base; 1814 alias base this; 1815 1816 int a; 1817 private string _b; 1818 1819 @safe pure nothrow @nogc: 1820 1821 string b() const @property { return _b; } 1822 void b(string b) @property { _b = b; } 1823 1824 int retArg(int v) { return v; } 1825 1826 this(int a, string b) 1827 { 1828 this.a = a; 1829 this._b = b; 1830 } 1831 } 1832 1833 static struct S 1834 { 1835 string b; 1836 int a; 1837 1838 double retArg(double v) { return v; } 1839 1840 // alias this members are supported 1841 Base base; 1842 alias base this; 1843 } 1844 1845 static void inc(ref int a) { a++; } 1846 1847 alias V = Nullable!(C, S); // or Variant! 1848 1849 auto v = V(2, "str"); 1850 assert(v._is!C); 1851 assert(v.a == 2); 1852 assert(v.b == "str"); 1853 // members are returned by reference if possible 1854 inc(v.a); 1855 assert(v.a == 3); 1856 v.b = "s"; 1857 assert(v.b == "s"); 1858 // alias this members are supported 1859 v.d = 10; 1860 assert(v.d == 10); 1861 // method call support 1862 assert(v.retArg(100)._is!int); 1863 assert(v.retArg(100) == 100); 1864 1865 v = V("S", 5); 1866 assert(v._is!S); 1867 assert(v.a == 5); 1868 assert(v.b == "S"); 1869 // members are returned by reference if possible 1870 inc(v.a); 1871 assert(v.a == 6); 1872 v.b = "s"; 1873 assert(v.b == "s"); 1874 // alias this members are supported 1875 v.d = 15; 1876 assert(v.d == 15); 1877 // method call support 1878 assert(v.retArg(300)._is!double); 1879 assert(v.retArg(300) == 300.0); 1880 1881 } 1882 1883 // test CTFE 1884 unittest 1885 { 1886 struct S { string s;} 1887 alias V = Nullable!(double, S); 1888 enum a = V(1.9); 1889 static assert (a == 1.9); 1890 enum b = V(S("str")); 1891 static assert(b == S("str")); 1892 static auto foo(int r) 1893 { 1894 auto s = V(S("str")); 1895 s = r; 1896 return s; 1897 } 1898 1899 static assert(foo(3) == 3); 1900 static auto bar(int r) 1901 { 1902 auto s = V(S("str")); 1903 s = r; 1904 return s.visit!((double d) => d, (_)=> 0.0)(); 1905 } 1906 assert(bar(3) == 3); 1907 static assert(bar(3) == 3); 1908 1909 static auto bar3(int r) 1910 { 1911 auto s = V(S("str")); 1912 s = r; 1913 return s.match!((double d) => d, (_)=> "3")(); 1914 } 1915 assert(bar(3) == 3); 1916 static assert(bar(3) == 3); 1917 } 1918 1919 @safe pure @nogc nothrow 1920 version(mir_core_test) unittest 1921 { 1922 import core.stdc..string: memcmp; 1923 1924 static struct C(ubyte payloadSize, bool isPOD, bool hasToHash = true, bool hasOpEquals = true) 1925 { 1926 ubyte[payloadSize] _payload; 1927 1928 const: 1929 1930 static if (!isPOD) 1931 { 1932 this(this) {} 1933 ~this() {} 1934 } 1935 1936 @safe pure nothrow @nogc: 1937 1938 1939 static if (hasToHash) 1940 size_t toHash() { return hashOf(_payload); } 1941 1942 static if (hasOpEquals) 1943 auto opEquals(ref const typeof(this) rhs) @trusted { return memcmp(_payload.ptr, rhs._payload.ptr, _payload.length); } 1944 auto opCmp(ref const typeof(this) rhs) { return _payload == rhs._payload; } 1945 } 1946 1947 static foreach (size1; [1, 2, 4, 8, 10, 16, 20]) 1948 static foreach (size2; [1, 2, 4, 8, 10, 16, 20]) 1949 static if (size1 != size2) 1950 static foreach (isPOD; [true, false]) 1951 static foreach (hasToHash; [true, false]) 1952 static foreach (hasOpEquals; [true, false]) 1953 {{ 1954 alias T = Variant!( 1955 double, 1956 C!(size1, isPOD, hasToHash, hasOpEquals), 1957 C!(size2, isPOD, hasToHash, hasOpEquals)); 1958 // static assert (__traits(compiles, T.init <= T.init)); 1959 }} 1960 } 1961 1962 // const propogation 1963 @safe pure nothrow @nogc 1964 version(mir_core_test) unittest 1965 { 1966 static struct S1 { immutable(ubyte)* value; } 1967 static struct C1 { immutable(uint)* value; } 1968 1969 alias V = Variant!(S1, C1); 1970 const V v = S1(); 1971 assert(v._is!S1); 1972 V w = v; 1973 w = v; 1974 1975 immutable f = V(S1()); 1976 auto t = immutable V(S1()); 1977 // auto j = immutable V(t); 1978 // auto i = const V(t); 1979 } 1980 1981 // ditto 1982 @safe pure nothrow @nogc 1983 version(mir_core_test) unittest 1984 { 1985 static struct S2 { 1986 uint* value; 1987 this(return ref scope const typeof(this) rhs) {} 1988 ref opAssign(typeof(this) rhs) return { return this; } 1989 } 1990 static struct C2 { const(uint)* value; } 1991 1992 alias V = Variant!(S2, C2); 1993 const V v = S2(); 1994 V w = v; 1995 w = S2(); 1996 w = v; 1997 w = cast(const) V.init; 1998 1999 const f = V(S2()); 2000 auto t = const V(f); 2001 } 2002 2003 @safe pure nothrow @nogc 2004 version(mir_core_test) unittest 2005 { 2006 static struct S3 { 2007 uint* value; 2008 this(return ref scope typeof(this) rhs) {} 2009 this(return ref scope const typeof(this) rhs) const {} 2010 this(return ref scope immutable typeof(this) rhs) immutable {} 2011 } 2012 static struct C3 { immutable(uint)* value; } 2013 2014 S3 s; 2015 S3 r = s; 2016 r = s; 2017 r = S3.init; 2018 2019 alias V = Variant!(S3, C3); 2020 V v = S3(); 2021 V w = v; 2022 w = S3(); 2023 w = V.init; 2024 w = v; 2025 2026 immutable V e = S3(); 2027 auto t = immutable V(S3()); 2028 auto j = const V(t); 2029 auto h = t; 2030 2031 immutable V l = C3(); 2032 auto g = immutable V(C3()); 2033 } 2034 2035 @safe pure nothrow @nogc 2036 version(mir_core_test) unittest 2037 { 2038 static struct S4 { 2039 uint* value; 2040 this(return ref scope const typeof(this) rhs) pure immutable {} 2041 } 2042 static struct C4 { immutable(uint)* value; } 2043 2044 2045 S4 s; 2046 S4 r = s; 2047 r = s; 2048 r = S4.init; 2049 2050 alias V = Variant!(S4, C4); 2051 V v = S4(); 2052 V w = v; 2053 w = S4(); 2054 w = V.init; 2055 w = v; 2056 2057 { 2058 const V e = S4(); 2059 const k = w; 2060 auto t = const V(k); 2061 auto j = immutable V(k); 2062 } 2063 2064 immutable V e = S4(); 2065 immutable k = w; 2066 auto t = immutable V(S4()); 2067 auto j = const V(t); 2068 auto h = t; 2069 2070 immutable V l = C4(); 2071 import core.lifetime; 2072 auto g = immutable V(C4()); 2073 immutable b = immutable V(s); 2074 } 2075 2076 @safe pure nothrow @nogc 2077 version(mir_core_test) unittest 2078 { 2079 import core.lifetime: move; 2080 2081 static struct S5 { 2082 immutable(uint)* value; 2083 this(return ref scope typeof(this) rhs) {} 2084 this(return ref scope const typeof(this) rhs) immutable {} 2085 } 2086 static struct C5 { immutable(uint)* value; } 2087 2088 S5 s; 2089 S5 r = s; 2090 r = s; 2091 r = S5.init; 2092 2093 alias V = Variant!(S5, C5); 2094 V v = S5(); 2095 V w = v; 2096 w = S5(); 2097 w = V.init; 2098 w = v; 2099 2100 immutable V e = S5(); 2101 immutable f = V(S5()); 2102 immutable k = w; 2103 auto t = immutable V(S5()); 2104 auto j = const V(t); 2105 auto h = t; 2106 2107 immutable V l = C5(); 2108 import core.lifetime; 2109 immutable n = w.move; 2110 auto g = immutable V(C5()); 2111 immutable b = immutable V(s); 2112 } 2113 2114 @safe pure nothrow @nogc 2115 version(mir_core_test) unittest 2116 { 2117 static struct S { 2118 uint* value; 2119 this(this) @safe pure nothrow @nogc {} 2120 // void opAssign(typeof(this) rhs) {} 2121 } 2122 static struct C { const(uint)* value; } 2123 2124 S s; 2125 S r = s; 2126 r = s; 2127 r = S.init; 2128 2129 alias V = Variant!(S, C); 2130 V v = S(); 2131 V w = v; 2132 w = S(); 2133 w = V.init; 2134 w = v; 2135 } 2136 2137 /++ 2138 Applies a delegate or function to the given Variant depending on the held type, 2139 ensuring that all types are handled by the visiting functions. 2140 +/ 2141 alias visit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, false); 2142 2143 /// 2144 @safe pure @nogc nothrow 2145 version(mir_core_test) unittest 2146 { 2147 alias Number = Variant!(int, double); 2148 2149 Number x = 23; 2150 Number y = 1.0; 2151 2152 assert(x.visit!((int v) => true, (float v) => false)); 2153 assert(y.visit!((int v) => false, (float v) => true)); 2154 } 2155 2156 /// 2157 @safe pure @nogc 2158 version(mir_core_test) unittest 2159 { 2160 alias Number = Nullable!(int, double); 2161 2162 Number z = null; // default 2163 Number x = 23; 2164 Number y = 1.0; 2165 2166 () nothrow { 2167 assert(x.visit!((int v) => true, (float v) => false)); 2168 assert(y.visit!((int v) => false, (v) => true)); 2169 assert(z.visit!((typeof(null) v) => true, (v) => false)); 2170 } (); 2171 2172 auto xx = x.get; 2173 static assert (is(typeof(xx) == Variant!(int, double))); 2174 assert(xx.visit!((int v) => v, (float v) => 0) == 23); 2175 assert(xx.visit!((ref v) => v) == 23); 2176 2177 x = null; 2178 y.nullify; 2179 2180 assert(x.isNull); 2181 assert(y.isNull); 2182 assert(z.isNull); 2183 assert(z == y); 2184 } 2185 2186 /++ 2187 Checks $(LREF .Algebraic.toString) and `void` 2188 $(LREF Algerbraic)`.toString` requries `mir-algorithm` package 2189 +/ 2190 @safe pure nothrow version(mir_core_test) unittest 2191 { 2192 import mir.conv: to; 2193 enum MIR_ALGORITHM = __traits(compiles, { import mir.format; }); 2194 2195 alias visitorHandler = visit!( 2196 (typeof(null)) => "NULL", 2197 () => "VOID", 2198 (ref r) {r += 1;}, // returns void 2199 ); 2200 2201 alias secondOrderVisitorHandler = visit!( 2202 () => "SO VOID", // void => to "RV VOID" 2203 (str) => str, // string to => it self 2204 ); 2205 2206 alias V = Nullable!(void, int); 2207 static assert(is(V == Variant!(typeof(null), void, int))); 2208 2209 V variant; 2210 2211 assert(secondOrderVisitorHandler(visitorHandler(variant)) == "NULL"); 2212 assert(variant.to!string == "null"); 2213 2214 variant = V._void; 2215 assert(variant._is!void); 2216 assert(is(typeof(variant.get!void()) == void)); 2217 2218 assert(secondOrderVisitorHandler(visitorHandler(variant)) == "VOID"); 2219 assert(variant.to!string == "void"); 2220 2221 variant = 5; 2222 2223 assert(secondOrderVisitorHandler(visitorHandler(variant)) == "SO VOID"); 2224 assert(variant == 6); 2225 assert(variant.to!string == (MIR_ALGORITHM ? "6" : "int")); 2226 } 2227 2228 /++ 2229 Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions. 2230 Throws: Exception if `naryFun!visitors` can't be called with provided arguments 2231 +/ 2232 alias tryVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.exception, false); 2233 2234 /// 2235 @safe pure @nogc 2236 version(mir_core_test) unittest 2237 { 2238 alias Number = Variant!(int, double); 2239 2240 Number x = 23; 2241 2242 assert(x.tryVisit!((int v) => true)); 2243 } 2244 2245 /++ 2246 Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions. 2247 Returns: nullable variant, null value is used if `naryFun!visitors` can't be called with provided arguments. 2248 +/ 2249 alias optionalVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.nullable, false); 2250 2251 /// 2252 @safe pure @nogc nothrow 2253 version(mir_core_test) unittest 2254 { 2255 static struct S { int a; } 2256 2257 Variant!(S, double) variant; 2258 2259 alias optionalVisitInst = optionalVisit!((ref value) => value + 0); 2260 2261 // do nothing because of variant isn't initialized 2262 Nullable!double result = optionalVisitInst(variant); 2263 assert(result.isNull); 2264 2265 variant = S(2); 2266 // do nothing because of lambda can't compile 2267 result = optionalVisitInst(variant); 2268 assert(result == null); 2269 2270 variant = 3.0; 2271 result = optionalVisitInst(variant); 2272 assert (result == 3.0); 2273 } 2274 2275 /++ 2276 Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions. 2277 Returns: optionally nullable type, null value is used if `naryFun!visitors` can't be called with provided arguments. 2278 +/ 2279 alias autoVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.auto_, false); 2280 2281 2282 /++ 2283 Applies a delegate or function to the given arguments depending on the held type, 2284 ensuring that all types are handled by the visiting functions. 2285 2286 The handler supports multiple dispatch or multimethods: a feature of handler in which 2287 a function or method can be dynamically dispatched based on the run time (dynamic) type or, 2288 in the more general case, some other attribute of more than one of its arguments. 2289 2290 Fuses algebraic types on return. 2291 2292 See_also: $(HTTPS en.wikipedia.org/wiki/Multiple_dispatch, Multiple dispatch) 2293 +/ 2294 alias match(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, true); 2295 2296 /// 2297 version(mir_core_test) 2298 unittest 2299 { 2300 struct Asteroid { uint size; } 2301 struct Spaceship { uint size; } 2302 alias SpaceObject = Variant!(Asteroid, Spaceship); 2303 2304 alias collideWith = match!( 2305 (Asteroid x, Asteroid y) => "a/a", 2306 (Asteroid x, Spaceship y) => "a/s", 2307 (Spaceship x, Asteroid y) => "s/a", 2308 (Spaceship x, Spaceship y) => "s/s", 2309 ); 2310 2311 import mir.utility: min; 2312 2313 // Direct access of a member in case of all algebraic types has this member 2314 alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1; 2315 2316 alias collide = (x, y) => oops(x, y) ? "big-boom" : collideWith(x, y); 2317 2318 auto ea = Asteroid(1); 2319 auto es = Spaceship(2); 2320 auto oa = SpaceObject(ea); 2321 auto os = SpaceObject(es); 2322 2323 // Asteroid-Asteroid 2324 assert(collide(ea, ea) == "a/a"); 2325 assert(collide(ea, oa) == "a/a"); 2326 assert(collide(oa, ea) == "a/a"); 2327 assert(collide(oa, oa) == "a/a"); 2328 2329 // Asteroid-Spaceship 2330 assert(collide(ea, es) == "a/s"); 2331 assert(collide(ea, os) == "a/s"); 2332 assert(collide(oa, es) == "a/s"); 2333 assert(collide(oa, os) == "a/s"); 2334 2335 // Spaceship-Asteroid 2336 assert(collide(es, ea) == "s/a"); 2337 assert(collide(es, oa) == "s/a"); 2338 assert(collide(os, ea) == "s/a"); 2339 assert(collide(os, oa) == "s/a"); 2340 2341 // Spaceship-Spaceship 2342 assert(collide(es, es) == "big-boom"); 2343 assert(collide(es, os) == "big-boom"); 2344 assert(collide(os, es) == "big-boom"); 2345 assert(collide(os, os) == "big-boom"); 2346 } 2347 2348 /++ 2349 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions. 2350 Throws: Exception if `naryFun!visitors` can't be called with provided arguments 2351 2352 Fuses algebraic types on return. 2353 +/ 2354 alias tryMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.exception, true); 2355 2356 /// 2357 version(mir_core_test) 2358 unittest 2359 { 2360 import std.exception: assertThrown; 2361 struct Asteroid { uint size; } 2362 struct Spaceship { uint size; } 2363 alias SpaceObject = Variant!(Asteroid, Spaceship); 2364 2365 alias collideWith = tryMatch!( 2366 (Asteroid x, Asteroid y) => "a/a", 2367 // No visitor for A/S pair 2368 // (Asteroid x, Spaceship y) => "a/s", 2369 (Spaceship x, Asteroid y) => "s/a", 2370 (Spaceship x, Spaceship y) => "s/s", 2371 ); 2372 2373 import mir.utility: min; 2374 // Direct access of a member in case of all algebraic types has this member 2375 alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1; 2376 2377 alias collide = (x, y) => oops(x, y) ? "big-boom" : collideWith(x, y); 2378 2379 auto ea = Asteroid(1); 2380 auto es = Spaceship(2); 2381 auto oa = SpaceObject(ea); 2382 auto os = SpaceObject(es); 2383 2384 // Asteroid-Asteroid 2385 assert(collide(ea, ea) == "a/a"); 2386 assert(collide(ea, oa) == "a/a"); 2387 assert(collide(oa, ea) == "a/a"); 2388 assert(collide(oa, oa) == "a/a"); 2389 2390 // Asteroid-Spaceship 2391 assertThrown!Exception(collide(ea, es)); 2392 assertThrown!Exception(collide(ea, os)); 2393 assertThrown!Exception(collide(oa, es)); 2394 assertThrown!Exception(collide(oa, os)); 2395 2396 // can deduce the type based on other return values 2397 static assert(is(typeof(collide(ea, os)) == string)); 2398 static assert(is(typeof(collide(oa, es)) == string)); 2399 static assert(is(typeof(collide(oa, os)) == string)); 2400 2401 // Also allows newer compilers to detect combinations which always throw an exception 2402 static if (is(typeof(collideWith(ea, es)) == noreturn)) 2403 { 2404 static assert(is(typeof(collide(ea, es)) == string)); 2405 } 2406 else 2407 { 2408 // not enough information to deduce the type from (ea, es) pair 2409 static assert(is(typeof(collide(ea, es)) == void)); 2410 } 2411 2412 // Spaceship-Asteroid 2413 assert(collide(es, ea) == "s/a"); 2414 assert(collide(es, oa) == "s/a"); 2415 assert(collide(os, ea) == "s/a"); 2416 assert(collide(os, oa) == "s/a"); 2417 2418 // Spaceship-Spaceship 2419 assert(collide(es, es) == "big-boom"); 2420 assert(collide(es, os) == "big-boom"); 2421 assert(collide(os, es) == "big-boom"); 2422 assert(collide(os, os) == "big-boom"); 2423 } 2424 2425 /++ 2426 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions. 2427 Returns: nullable variant, null value is used if `naryFun!visitors` can't be called with provided arguments. 2428 2429 Fuses algebraic types on return. 2430 +/ 2431 alias optionalMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.nullable, true); 2432 2433 /// 2434 version(mir_core_test) 2435 unittest 2436 { 2437 struct Asteroid { uint size; } 2438 struct Spaceship { uint size; } 2439 alias SpaceObject = Variant!(Asteroid, Spaceship); 2440 2441 alias collideWith = optionalMatch!( 2442 (Asteroid x, Asteroid y) => "a/a", 2443 // No visitor for A/S pair 2444 // (Asteroid x, Spaceship y) => "a/s", 2445 (Spaceship x, Asteroid y) => "s/a", 2446 (Spaceship x, Spaceship y) => "s/s", 2447 ); 2448 2449 import mir.utility: min; 2450 // Direct access of a member in case of all algebraic types has this member 2451 alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1; 2452 2453 alias collide = (x, y) => oops(x, y) ? "big-boom".nullable : collideWith(x, y); 2454 2455 auto ea = Asteroid(1); 2456 auto es = Spaceship(2); 2457 auto oa = SpaceObject(ea); 2458 auto os = SpaceObject(es); 2459 2460 // Asteroid-Asteroid 2461 assert(collide(ea, ea) == "a/a"); 2462 assert(collide(ea, oa) == "a/a"); 2463 assert(collide(oa, ea) == "a/a"); 2464 assert(collide(oa, oa) == "a/a"); 2465 2466 // Asteroid-Spaceship 2467 // assert(collide(ea, es).isNull); // Compiler error: incompatible types 2468 assert(collideWith(ea, es).isNull); // OK 2469 assert(collide(ea, os).isNull); 2470 assert(collide(oa, es).isNull); 2471 assert(collide(oa, os).isNull); 2472 2473 2474 // Spaceship-Asteroid 2475 assert(collide(es, ea) == "s/a"); 2476 assert(collide(es, oa) == "s/a"); 2477 assert(collide(os, ea) == "s/a"); 2478 assert(collide(os, oa) == "s/a"); 2479 2480 // Spaceship-Spaceship 2481 assert(collide(es, es) == "big-boom"); 2482 assert(collide(es, os) == "big-boom"); 2483 assert(collide(os, es) == "big-boom"); 2484 assert(collide(os, os) == "big-boom"); 2485 2486 // check types 2487 2488 static assert(!__traits(compiles, collide(Asteroid.init, Spaceship.init))); 2489 static assert(is(typeof(collideWith(Asteroid.init, Spaceship.init)) == Nullable!())); 2490 2491 static assert(is(typeof(collide(Asteroid.init, Asteroid.init)) == Nullable!string)); 2492 static assert(is(typeof(collide(Asteroid.init, SpaceObject.init)) == Nullable!string)); 2493 static assert(is(typeof(collide(SpaceObject.init, Asteroid.init)) == Nullable!string)); 2494 static assert(is(typeof(collide(SpaceObject.init, SpaceObject.init)) == Nullable!string)); 2495 static assert(is(typeof(collide(SpaceObject.init, Spaceship.init)) == Nullable!string)); 2496 static assert(is(typeof(collide(Spaceship.init, Asteroid.init)) == Nullable!string)); 2497 static assert(is(typeof(collide(Spaceship.init, SpaceObject.init)) == Nullable!string)); 2498 static assert(is(typeof(collide(Spaceship.init, Spaceship.init)) == Nullable!string)); 2499 } 2500 2501 /++ 2502 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions. 2503 Returns: optionally nullable type, null value is used if `naryFun!visitors` can't be called with provided arguments. 2504 2505 Fuses algebraic types on return. 2506 +/ 2507 alias autoMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.auto_, true); 2508 2509 /// 2510 version(mir_core_test) 2511 unittest 2512 { 2513 struct Asteroid { uint size; } 2514 struct Spaceship { uint size; } 2515 alias SpaceObject = Variant!(Asteroid, Spaceship); 2516 2517 alias collideWith = autoMatch!( 2518 (Asteroid x, Asteroid y) => "a/a", 2519 // No visitor for A/S pair 2520 // (Asteroid x, Spaceship y) => "a/s", 2521 (Spaceship x, Asteroid y) => "s/a", 2522 (Spaceship x, Spaceship y) => "s/s", 2523 ); 2524 2525 import mir.utility: min; 2526 // Direct access of a member in case of all algebraic types has this member 2527 alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1; 2528 2529 import mir.conv: to; 2530 alias collide = (x, y) => oops(x, y) ? "big-boom".to!(typeof(collideWith(x, y))) : collideWith(x, y); 2531 2532 auto ea = Asteroid(1); 2533 auto es = Spaceship(2); 2534 auto oa = SpaceObject(ea); 2535 auto os = SpaceObject(es); 2536 2537 // Asteroid-Asteroid 2538 assert(collide(ea, ea) == "a/a"); 2539 assert(collide(ea, oa) == "a/a"); 2540 assert(collide(oa, ea) == "a/a"); 2541 assert(collide(oa, oa) == "a/a"); 2542 2543 // Asteroid-Spaceship 2544 // assert(collide(ea, es).isNull); // Compiler error: incompatible types 2545 assert(collideWith(ea, es).isNull); // OK 2546 assert(collide(ea, os).isNull); 2547 assert(collide(oa, es).isNull); 2548 assert(collide(oa, os).isNull); 2549 2550 // Spaceship-Asteroid 2551 assert(collide(es, ea) == "s/a"); 2552 assert(collide(es, oa) == "s/a"); 2553 assert(collide(os, ea) == "s/a"); 2554 assert(collide(os, oa) == "s/a"); 2555 2556 // Spaceship-Spaceship 2557 assert(collide(es, es) == "big-boom"); 2558 assert(collide(es, os) == "big-boom"); 2559 assert(collide(os, es) == "big-boom"); 2560 assert(collide(os, os) == "big-boom"); 2561 2562 // check types 2563 2564 static assert(!__traits(compiles, collide(Asteroid.init, Spaceship.init))); 2565 static assert(is(typeof(collideWith(Asteroid.init, Spaceship.init)) == Nullable!())); 2566 2567 static assert(is(typeof(collide(Asteroid.init, Asteroid.init)) == string)); 2568 static assert(is(typeof(collide(SpaceObject.init, Asteroid.init)) == string)); 2569 static assert(is(typeof(collide(Spaceship.init, Asteroid.init)) == string)); 2570 static assert(is(typeof(collide(Spaceship.init, SpaceObject.init)) == string)); 2571 static assert(is(typeof(collide(Spaceship.init, Spaceship.init)) == string)); 2572 2573 static assert(is(typeof(collide(Asteroid.init, SpaceObject.init)) == Nullable!string)); 2574 static assert(is(typeof(collide(SpaceObject.init, SpaceObject.init)) == Nullable!string)); 2575 static assert(is(typeof(collide(SpaceObject.init, Spaceship.init)) == Nullable!string)); 2576 } 2577 2578 /++ 2579 Applies a member handler to the given Variant depending on the held type, 2580 ensuring that all types are handled by the visiting handler. 2581 +/ 2582 alias getMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.compileTime, false); 2583 2584 /// 2585 @safe pure @nogc nothrow 2586 version(mir_core_test) unittest 2587 { 2588 static struct S { auto bar(int a) { return a; } enum boolean = true; } 2589 static struct C { alias bar = (double a) => a * 2; enum boolean = false; } 2590 2591 alias V = Variant!(S, C); 2592 2593 V x = S(); 2594 V y = C(); 2595 2596 static assert(is(typeof(x.getMember!"bar"(2)) == Variant!(int, double))); 2597 assert(x.getMember!"bar"(2) == 2); 2598 assert(y.getMember!"bar"(2) != 4); 2599 assert(y.getMember!"bar"(2) == 4.0); 2600 2601 // direct implementation 2602 assert(x.bar(2) == 2); 2603 assert(y.bar(2) != 4); 2604 assert(y.bar(2) == 4.0); 2605 assert(x.boolean); 2606 assert(!y.boolean); 2607 } 2608 2609 /++ 2610 Applies a member handler to the given Variant depending on the held type, 2611 ensuring that all types are handled by the visiting handler. 2612 2613 Fuses algebraic types on return. 2614 +/ 2615 alias matchMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.compileTime, true); 2616 2617 /// 2618 @safe pure @nogc nothrow 2619 version(mir_core_test) unittest 2620 { 2621 static struct S 2622 { 2623 Nullable!int m; 2624 } 2625 2626 static struct C 2627 { 2628 Variant!(float, double) m; 2629 } 2630 2631 alias V = Variant!(S, C); 2632 2633 V x = S(2.nullable); 2634 V y = C(Variant!(float, double)(4.0)); 2635 2636 // getMember returns an algebraic of algebaics 2637 static assert(is(typeof(x.getMember!"m") == Variant!(Variant!(float, double), Nullable!int))); 2638 // matchMember returns a fused algebraic 2639 static assert(is(typeof(x.matchMember!"m") == Nullable!(int, float, double))); 2640 assert(x.matchMember!"m" == 2); 2641 assert(y.matchMember!"m" != 4); 2642 assert(y.matchMember!"m" == 4.0); 2643 } 2644 2645 /++ 2646 Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor. 2647 Throws: Exception if member can't be accessed with provided arguments 2648 +/ 2649 alias tryGetMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.exception, false); 2650 2651 /// 2652 @safe pure @nogc 2653 version(mir_core_test) unittest 2654 { 2655 static struct S { int bar(int a) { return a; }} 2656 static struct C { alias Bar = (double a) => a * 2; } 2657 2658 alias V = Variant!(S, C); 2659 2660 V x = S(); 2661 V y = C(); 2662 2663 static assert(is(typeof(x.tryGetMember!"bar"(2)) == int)); 2664 static assert(is(typeof(y.tryGetMember!"Bar"(2)) == double)); 2665 assert(x.tryGetMember!"bar"(2) == 2); 2666 assert(y.tryGetMember!"Bar"(2) == 4.0); 2667 } 2668 2669 /// 2670 @safe pure @nogc nothrow 2671 version(mir_core_test) unittest 2672 { 2673 alias Number = Variant!(int, double); 2674 2675 Number x = Number(23); 2676 Number y = Number(1.0); 2677 2678 assert(x.visit!((int v) => true, (float v) => false)); 2679 assert(y.visit!((int v) => false, (float v) => true)); 2680 } 2681 2682 /++ 2683 Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor. 2684 Throws: Exception if member can't be accessed with provided arguments 2685 2686 Fuses algebraic types on return. 2687 +/ 2688 alias tryMatchMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.exception, true); 2689 2690 /++ 2691 Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor. 2692 Returns: nullable variant, null value is used if the member can't be called with provided arguments. 2693 +/ 2694 alias optionalGetMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.nullable, false); 2695 2696 /++ 2697 Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor. 2698 Returns: nullable variant, null value is used if the member can't be called with provided arguments. 2699 2700 Fuses algebraic types on return. 2701 +/ 2702 alias optionalMatchMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.nullable, true); 2703 2704 /++ 2705 Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor. 2706 Returns: optionally nullable type, null value is used if the member can't be called with provided arguments. 2707 +/ 2708 alias autoGetMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.auto_, false); 2709 2710 /++ 2711 Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor. 2712 Returns: optionally nullable type, null value is used if the member can't be called with provided arguments. 2713 2714 Fuses algebraic types on return. 2715 +/ 2716 alias autoMatchMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.auto_, true); 2717 2718 private template getMemberHandler(string member) 2719 { 2720 /// 2721 auto ref getMemberHandler(V, Args...)(ref V value, auto ref Args args) 2722 { 2723 static if (Args.length == 0) 2724 { 2725 return __traits(getMember, value, member); 2726 } 2727 else 2728 { 2729 import core.lifetime: forward; 2730 import mir.reflection: hasField; 2731 static if (hasField!(V, member) && Args.length == 1) 2732 return __traits(getMember, value, member) = forward!args; 2733 else 2734 return __traits(getMember, value, member)(forward!args); 2735 } 2736 } 2737 } 2738 2739 private template VariantReturnTypes(T...) 2740 { 2741 import std.meta: staticMap; 2742 2743 alias VariantReturnTypes = NoDuplicates!(staticMap!(TryRemoveConst, T)); 2744 } 2745 2746 private enum Exhaustive 2747 { 2748 compileTime, 2749 exception, 2750 nullable, 2751 auto_, 2752 } 2753 2754 private template nextVisitor(T, alias visitor, alias arg) 2755 { 2756 static if (is(T == void)) 2757 { 2758 alias nextVisitor = visitor; 2759 } 2760 else 2761 auto ref nextVisitor(NextArgs...)(auto ref NextArgs nextArgs) 2762 { 2763 import core.lifetime: forward; 2764 return visitor(arg.trustedGet!T, forward!nextArgs); 2765 } 2766 } 2767 2768 private template nextVisitor(alias visitor, alias arg) 2769 { 2770 auto ref nextVisitor(NextArgs...)(auto ref NextArgs nextArgs) 2771 { 2772 import core.lifetime: forward; 2773 return visitor(forward!arg, forward!nextArgs); 2774 } 2775 } 2776 2777 private template visitThis(alias visitor, Exhaustive nextExhaustive, args...) 2778 { 2779 auto ref visitThis(T, Args...)(auto ref Args args) 2780 { 2781 import core.lifetime: forward; 2782 return .visitImpl!(nextVisitor!(T, visitor, forward!(args[0])), nextExhaustive, true)(forward!(args[1 .. $])); 2783 } 2784 } 2785 2786 private template visitLast(alias visitor, args...) 2787 { 2788 auto ref visitLast(T, Args...)(auto ref Args args) 2789 { 2790 import core.lifetime: forward; 2791 static if (is(T == void)) 2792 return visitor(forward!(args[1 .. $])); 2793 else 2794 return visitor(args[0].trustedGet!T, forward!(args[1 .. $])); 2795 } 2796 } 2797 2798 private template visitImpl(alias visitor, Exhaustive exhaustive, bool fused) 2799 { 2800 import std.meta: anySatisfy, staticMap, AliasSeq; 2801 2802 /// 2803 auto ref visitImpl(Args...)(auto ref Args args) 2804 { 2805 import core.lifetime: forward; 2806 2807 static if (!anySatisfy!(isVariant, Args)) 2808 { 2809 static if (exhaustive == Exhaustive.compileTime) 2810 { 2811 return visitor(forward!args); 2812 } 2813 else 2814 static if (exhaustive == Exhaustive.exception) 2815 { 2816 static if (__traits(compiles, visitor(forward!args))) 2817 return visitor(forward!args); 2818 else 2819 throw variantMemberException; 2820 } 2821 else 2822 static if (exhaustive == Exhaustive.nullable) 2823 { 2824 static if (__traits(compiles, visitor(forward!args))) 2825 return Nullable!(typeof(visitor(forward!args)))(visitor(forward!args)); 2826 else 2827 return Nullable!().init; 2828 } 2829 else 2830 static if (exhaustive == Exhaustive.auto_) 2831 { 2832 static if (__traits(compiles, visitor(forward!args))) 2833 return visitor(forward!args); 2834 else 2835 return Nullable!().init; 2836 } 2837 else 2838 static assert(0, "not implemented"); 2839 } 2840 else 2841 static if (!isVariant!(Args[0])) 2842 { 2843 return .visitImpl!(nextVisitor!(visitor, args[0]), exhaustive, fused)(forward!(args[1 .. $])); 2844 } 2845 else 2846 { 2847 static if (fused && anySatisfy!(isVariant, Args[1 .. $])) 2848 { 2849 alias fun = visitThis!(visitor, exhaustive); 2850 } 2851 else 2852 { 2853 static assert (isVariant!(Args[0]), "First argument should be a Mir Algebraic type"); 2854 alias fun = visitLast!visitor; 2855 } 2856 2857 template VariantReturnTypesImpl(T) 2858 { 2859 static if (__traits(compiles, fun!T(forward!args))) 2860 static if (fused && is(typeof(fun!T(forward!args)) : Algebraic!Types, Types...)) 2861 alias VariantReturnTypesImpl = TryRemoveConst!(typeof(fun!T(forward!args))).AllowedTypes; 2862 else 2863 alias VariantReturnTypesImpl = AliasSeq!(TryRemoveConst!(typeof(fun!T(forward!args)))); 2864 else 2865 static if (exhaustive == Exhaustive.auto_) 2866 alias VariantReturnTypesImpl = AliasSeq!(typeof(null)); 2867 else 2868 alias VariantReturnTypesImpl = AliasSeq!(); 2869 } 2870 2871 static if (exhaustive == Exhaustive.nullable) 2872 alias AllReturnTypes = NoDuplicates!(typeof(null), staticMap!(VariantReturnTypesImpl, Args[0].AllowedTypes)); 2873 else 2874 alias AllReturnTypes = NoDuplicates!(staticMap!(VariantReturnTypesImpl, Args[0].AllowedTypes)); 2875 2876 switch (args[0]._identifier_) 2877 { 2878 static foreach (i, T; Args[0].AllowedTypes) 2879 { 2880 case i: 2881 static if (__traits(compiles, fun!T(forward!args)) || exhaustive == Exhaustive.compileTime && !is(T == typeof(null))) 2882 { 2883 static if (AllReturnTypes.length == 1) 2884 { 2885 return fun!T(forward!args); 2886 } 2887 else 2888 static if (is(VariantReturnTypesImpl!T == AliasSeq!void)) 2889 { 2890 fun!T(forward!args); 2891 return Variant!AllReturnTypes._void; 2892 } 2893 else 2894 static if (is(typeof(fun!T(forward!args)) == Variant!AllReturnTypes)) 2895 { 2896 return fun!T(forward!args); 2897 } 2898 else 2899 { 2900 return Variant!AllReturnTypes(fun!T(forward!args)); 2901 } 2902 } 2903 else 2904 static if (exhaustive == Exhaustive.compileTime && is(T == typeof(null))) 2905 { 2906 assert(0, "Null " ~ Args[0].stringof); 2907 } 2908 else 2909 static if (exhaustive == Exhaustive.nullable || exhaustive == Exhaustive.auto_) 2910 { 2911 return Variant!AllReturnTypes(null); 2912 } 2913 else 2914 { 2915 throw variantMemberException; 2916 } 2917 } 2918 default: assert(0); 2919 } 2920 } 2921 } 2922 } 2923 2924 private string enumKindText()(string[] strs) 2925 { 2926 auto r = "enum Kind {"; 2927 foreach (s; strs) 2928 { 2929 r ~= s; 2930 r ~= ", "; 2931 } 2932 r ~= "}"; 2933 return r; 2934 } 2935 2936 @safe pure @nogc 2937 version(mir_core_test) unittest 2938 { 2939 static struct S { int a; } 2940 2941 Variant!(S, double) variant; 2942 variant = 1.0; 2943 variant.tryVisit!((ref value, b) => value += b)(2); 2944 assert (variant.get!double == 3); 2945 2946 alias fun = (ref value) { 2947 static if (is(typeof(value) == S)) 2948 value.a += 2; 2949 else 2950 value += 2; 2951 }; 2952 2953 variant.tryVisit!fun; 2954 assert (variant.get!double == 5); 2955 2956 variant = S(4); 2957 variant.tryVisit!fun; 2958 assert (variant.get!S.a == 6); 2959 2960 alias D = Variant!(Variant!(S, double)); 2961 } 2962 2963 @safe pure @nogc 2964 version(mir_core_test) unittest 2965 { 2966 import std.meta: AliasSeq; 2967 2968 static struct PODWithLongPointer { 2969 long* x; 2970 this(long l) pure 2971 { 2972 x = new long(l); 2973 } 2974 2975 @property: 2976 long a() const { 2977 return x ? *x : 0; 2978 } 2979 2980 void a(long l) { 2981 if (x) { 2982 *x = l; 2983 } else { 2984 x = new long(l); 2985 } 2986 } 2987 } 2988 static assert(is(TypeSet!(byte, immutable PODWithLongPointer) == AliasSeq!(byte, immutable PODWithLongPointer))); 2989 } 2990 2991 private enum isSimpleAggregateType(T) = is(T == class) || is(T == struct) || is(T == union) || is(T == interface); 2992 2993 unittest 2994 { 2995 static struct Test 2996 { 2997 alias Value = void; 2998 } 2999 3000 alias B = Nullable!Test; 3001 }