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 Fuses Algebraic types on return)) 27 $(LEADINGROWN 8, Classic handlers) 28 $(T8 visit, Yes, N/A, No, No, 1+, No) 29 $(T8 optionalVisit, No, No, Yes, No, 1+, No) 30 $(T8 autoVisit, No, No, auto, No, 1+, No) 31 $(T8 tryVisit, No, Yes, No, No, 1+, No) 32 $(LEADINGROWN 8, Multiple dispatch and algebraic fusion on return) 33 $(T8 match, Yes, N/A, No, Yes, 0+, Yes) 34 $(T8 optionalMatch, No, No, Yes, Yes, 0+, Yes) 35 $(T8 autoMatch, No, No, auto, Yes, 0+, Yes) 36 $(T8 tryMatch, No, Yes, No, Yes, 0+, Yes) 37 $(LEADINGROWN 8, Inner handlers. Multiple dispatch and algebraic fusion on return.) 38 $(T8 suit, N/A(Yes), N/A, No, Yes, ?, Yes) 39 $(T8 some, N/A(Yes), N/A, No, Yes, 0+, Yes) 40 $(T8 none, N/A(Yes), N/A, No, Yes, 1+, Yes) 41 $(T8 assumeOk, Yes(No), No(Yes), No(Yes), Yes(No), 0+, Yes(No)) 42 $(LEADINGROWN 8, Member access) 43 $(T8 getMember, Yes, N/A, No, No, 1+, No) 44 $(T8 optionalGetMember, No, No, Yes, No, 1+, No) 45 $(T8 autoGetMember, No, No, auto, No, 1+, No) 46 $(T8 tryGetMember, No, Yes, No, No, 1+, No) 47 $(LEADINGROWN 8, Member access with algebraic fusion on return) 48 $(T8 matchMember, Yes, N/A, No, No, 1+, Yes) 49 $(T8 optionalMatchMember, No, No, Yes, No, 1+, Yes) 50 $(T8 autoMatchMember, No, No, auto, No, 1+, Yes) 51 $(T8 tryMatchMember, No, Yes, No, No, 1+, Yes) 52 ) 53 54 $(BOOKTABLE $(H3 Special Types), 55 $(TR $(TH Name) $(TH Description)) 56 $(T2plain `void`, It is usefull to indicate a possible return type of the visitor. Can't be accesed by reference. ) 57 $(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. ) 58 $(T2 This, Dummy structure that is used to construct self-referencing algebraic types. Example: `Variant!(int, double, string, This*[2])`) 59 $(T2plain $(LREF SetAlias)`!setId`, Dummy structure that is used to construct cyclic-referencing lists of algebraic types. ) 60 $(T2 Err, Wrapper to denote an error value type. ) 61 $(T2 reflectErr, Attribute that denotes that the type is an error value type. ) 62 ) 63 64 $(BOOKTABLE $(H3 $(LREF Algebraic) Traits), 65 $(TR $(TH Name) $(TH Description)) 66 $(T2 isVariant, Checks if the type is instance of $(LREF Algebraic).) 67 $(T2 isNullable, Checks if the type is instance of $(LREF Algebraic) with a self $(LREF TypeSet) that contains `typeof(null)`. ) 68 $(T2 isTypeSet, Checks if the types are the same as $(LREF TypeSet) of them. ) 69 $(T2 ValueTypeOfNullable, Gets type of $(LI $(LREF .Algebraic.get.2)) method. ) 70 $(T2 SomeVariant, Gets subtype of algebraic without types for which $(LREF isErr) is true.) 71 $(T2 NoneVariant, Gets subtype of algebraic with types for which $(LREF isErr) is true.) 72 $(T2 isErr, Checks if T is a instance of $(LREF Err) or if it is annotated with $(LREF reflectErr).) 73 $(T2 isResultVariant, Checks if T is a Variant with at least one allowed type that satisfy $(LREF isErr) traits.) 74 75 ) 76 77 78 $(H3 Type Set) 79 $(UL 80 $(LI Type set is unordered. Example:`TypeSet!(int, double)` and `TypeSet!(double, int)` are the same. ) 81 $(LI Duplicats are ignored. Example: `TypeSet!(float, int, float)` and `TypeSet!(int, float)` are the same. ) 82 $(LI Types are automatically unqualified if this operation can be performed implicitly. Example: `TypeSet!(const int) and `TypeSet!int` are the same. ) 83 $(LI Non trivial `TypeSet!(A, B, ..., etc)` is allowed.) 84 $(LI Trivial `TypeSet!T` is allowed.) 85 $(LI Empty `TypeSet!()` is allowed.) 86 ) 87 88 $(H3 Visitors) 89 $(UL 90 $(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. ) 91 $(LI Visitors are allowed to accept additional arguments. The arguments can be passed to the visitor handler. ) 92 $(LI Multiple visitors can be passes to the visitor handler. ) 93 $(LI Visitors are matched according to the common $(HTTPS dlang.org/spec/function.html#function-overloading, Dlang Function Overloading) rules. ) 94 $(LI Visitors are allowed accept algebraic value by reference except the value of `typeof(null)`. ) 95 $(LI Visitors are called without algebraic value if its algebraic type is `void`. ) 96 $(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. ) 97 ) 98 99 $(H3 Implementation Features) 100 $(UL 101 $(LI BetterC support. Runtime `TypeInfo` is not used.) 102 $(LI Copy-constructors and postblit constructors are supported. ) 103 $(LI `toHash`, `opCmp`. `opEquals`, and `toString` support. ) 104 $(LI No string or template mixins are used. ) 105 $(LI Optimised for fast execution. ) 106 $(LI $(LREF some) / $(LREF none) idiom. ) 107 ) 108 109 See_also: $(HTTPS en.wikipedia.org/wiki/Algebra_of_sets, Algebra of sets). 110 111 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 112 Authors: Ilia Ki 113 114 Macros: 115 T2plain=$(TR $(TDNW $1) $(TD $+)) 116 T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 117 T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4)) 118 T8=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4) $(TD $5) $(TD $6) $(TD $7) $(TD $8)) 119 120 +/ 121 module mir.algebraic; 122 123 import mir.internal.meta; 124 import mir.functional: naryFun; 125 import mir.exception: toMutable; 126 127 /++ 128 The attribute is used to define a permanent member field in an anlgebraic type. 129 Should applied to a field of the union passed to $(LREF TaggedVariant). 130 +/ 131 enum algMeta; 132 /++ 133 The attribute is used in pair with $(LREF algMeta) to exclude the field 134 from compression in `toHash`, `opEquals`, and `opCmp` methods. 135 +/ 136 enum algTransp; 137 /++ 138 The attribute is used in pair with $(LREF algMeta) to use the field 139 as an error infomration. Usually it is a position marker in a file. 140 The type should have `scope const` `toString` method. 141 +/ 142 enum algVerbose; 143 144 private static immutable variantExceptionMsg = "mir.algebraic: the algebraic stores other type then requested."; 145 private static immutable variantNullExceptionMsg = "mir.algebraic: the algebraic is empty and doesn't store any value."; 146 private static immutable variantMemberExceptionMsg = "mir.algebraic: the algebraic stores a type that isn't compatible with the user provided visitor and arguments."; 147 148 version (D_Exceptions) 149 { 150 private static immutable variantException = new Exception(variantExceptionMsg); 151 private static immutable variantNullException = new Exception(variantNullExceptionMsg); 152 private static immutable variantMemberException = new Exception(variantMemberExceptionMsg); 153 } 154 155 private static struct _Null() 156 { 157 @safe pure nothrow @nogc const: 158 int opCmp(_Null) { return 0; } 159 this(typeof(null)) inout {} 160 string toString() { return "null"; } 161 } 162 163 private static struct _Void() 164 { 165 @safe pure nothrow @nogc const: 166 int opCmp(_Void) { return 0; } 167 string toString() { return "void"; } 168 } 169 170 /++ 171 Checks if the type is instance of $(LREF Algebraic). 172 +/ 173 enum bool isVariant(T) = is(immutable T == immutable Algebraic!Types, Types...); 174 175 /// 176 @safe pure version(mir_core_test) unittest 177 { 178 static assert(isVariant!(Variant!(int, string))); 179 static assert(isVariant!(const Variant!(int[], string))); 180 static assert(isVariant!(Nullable!(int, string))); 181 static assert(!isVariant!int); 182 } 183 184 /++ 185 Same as $(LREF isVariant), but matches for `alias this` variant types (requires 186 DMD FE 2.100.0 or later) 187 +/ 188 enum bool isLikeVariant(T) = !is(immutable T == immutable noreturn) 189 && is(immutable T : immutable Algebraic!Types, Types...); 190 191 192 static if (__VERSION__ >= 2_100) 193 { 194 /// 195 @safe pure version(mir_core_test) unittest 196 { 197 static struct CustomVariant 198 { 199 Variant!(int, string) data; 200 alias data this; 201 this(T)(T v) { data = v; } 202 ref typeof(this) opAssign(T)(T v) 203 { 204 data = v; 205 return this; 206 } 207 } 208 209 static assert(isLikeVariant!(Variant!(int, string))); 210 static assert(isLikeVariant!(const Variant!(int[], string))); 211 static assert(isLikeVariant!(Nullable!(int, string))); 212 static assert(!isLikeVariant!int); 213 214 static assert(!isVariant!CustomVariant); 215 static assert(isLikeVariant!CustomVariant); 216 217 CustomVariant customVariant = 5; 218 assert(customVariant.match!( 219 (string s) => false, 220 (int n) => true 221 )); 222 } 223 } 224 225 /++ 226 Checks if the type is instance of tagged $(LREF Algebraic). 227 228 Tagged algebraics can be defined with $(LREF TaggedVariant). 229 +/ 230 enum bool isTaggedVariant(T) = is(immutable T == immutable Algebraic!U, U) && is(U == union); 231 232 /// 233 @safe pure version(mir_core_test) unittest 234 { 235 static union MyUnion 236 { 237 int integer; 238 immutable(char)[] string; 239 } 240 241 alias MyAlgebraic = Algebraic!MyUnion; 242 static assert(isTaggedVariant!MyAlgebraic); 243 244 static assert(!isTaggedVariant!int); 245 static assert(!isTaggedVariant!(Variant!(int, string))); 246 } 247 248 /++ 249 Same as $(LREF isTaggedVariant), but with support for custom `alias this` 250 variants. 251 252 Only works since DMD FE 2.100, see $(LREF isLikeVariant). 253 +/ 254 enum bool isLikeTaggedVariant(T) = isLikeVariant!T && is(T.Kind == enum); 255 256 /++ 257 Checks if the type is instance of $(LREF Algebraic) with a self $(LREF TypeSet) that contains `typeof(null)`. 258 +/ 259 enum bool isNullable(T) = is(immutable T == immutable Algebraic!(typeof(null), Types), Types...); 260 261 /// 262 @safe pure version(mir_core_test) unittest 263 { 264 static assert(isNullable!(const Nullable!(int, string))); 265 static assert(isNullable!(Nullable!())); 266 267 static assert(!isNullable!(Variant!())); 268 static assert(!isNullable!(Variant!string)); 269 static assert(!isNullable!int); 270 static assert(!isNullable!string); 271 } 272 273 /++ 274 Same as $(LREF isNullable), but with support for custom `alias this` variants. 275 276 Only works since DMD FE 2.100, see $(LREF isLikeVariant). 277 +/ 278 enum bool isLikeNullable(T) = !is(immutable T == immutable noreturn) 279 && is(immutable T : immutable Algebraic!(typeof(null), Types), Types...); 280 281 /++ 282 Gets type of $(LI $(LREF .Algebraic.get.2)) method. 283 +/ 284 template ValueTypeOfNullable(T : Algebraic!(typeof(null), Types), Types...) 285 { 286 static if (Types.length == 1) 287 alias ValueTypeOfNullable = Types[0]; 288 else 289 alias ValueTypeOfNullable = Algebraic!Types; 290 } 291 292 /// 293 @safe pure version(mir_core_test) unittest 294 { 295 static assert(is(ValueTypeOfNullable!(const Nullable!(int, string)) == Algebraic!(int, string))); 296 static assert(is(ValueTypeOfNullable!(Nullable!()) == Algebraic!())); 297 static assert(is(typeof(Nullable!().get()) == Algebraic!())); 298 } 299 300 /++ 301 Dummy type for $(LREF Variant) and $(LREF Nullable) self-referencing. 302 +/ 303 struct This 304 { 305 @safe pure nothrow @nogc scope const: 306 int opCmp(typeof(this)) { return 0; } 307 string toString() { return typeof(this).stringof; } 308 } 309 310 private template TagInfo(T, string name, udas...) 311 if (udas.length <= 3) 312 { 313 import std.meta: staticIndexOf; 314 alias Type = T; 315 enum tag = name; 316 enum meta = staticIndexOf!(algMeta, udas) >= 0; 317 enum transparent = staticIndexOf!(algTransp, udas) >= 0; 318 enum verbose = staticIndexOf!(algVerbose, udas) >= 0; 319 } 320 321 // example from std.variant 322 /++ 323 $(H4 Self-Referential Types) 324 A useful and popular use of algebraic data structures is for defining 325 $(LUCKY self-referential data structures), i.e. structures that embed references to 326 values of their own type within. 327 This is achieved with $(LREF Variant) by using $(LREF This) as a placeholder whenever a 328 reference to the type being defined is needed. The $(LREF Variant) instantiation 329 will perform 330 $(LINK2 https://en.wikipedia.org/wiki/Name_resolution_(programming_languages)#Alpha_renaming_to_make_name_resolution_trivial, 331 alpha renaming) on its constituent types, replacing $(LREF This) 332 with the self-referenced type. The structure of the type involving $(LREF This) may 333 be arbitrarily complex. 334 +/ 335 @safe pure version(mir_core_test) unittest 336 { 337 import mir.functional: Tuple; 338 339 // A tree is either a leaf or a branch of two others 340 alias Tree(Leaf) = Variant!(Leaf, Tuple!(This*, This*)); 341 alias Leafs = Tuple!(Tree!int*, Tree!int*); 342 343 Tree!int tree = Leafs(new Tree!int(41), new Tree!int(43)); 344 Tree!int* right = tree.get!Leafs[1]; 345 assert(*right == 43); 346 } 347 348 /// 349 @safe pure version(mir_core_test) unittest 350 { 351 // An object is a double, a string, or a hash of objects 352 alias Obj = Variant!(double, string, This[string], This[]); 353 alias Map = Obj[string]; 354 355 Obj obj = "hello"; 356 assert(obj._is!string); 357 assert(obj.trustedGet!string == "hello"); 358 obj = 42.0; 359 assert(obj.get!double == 42); 360 obj = ["customer": Obj("John"), "paid": Obj(23.95)]; 361 assert(obj.get!Map["customer"] == "John"); 362 } 363 364 /++ 365 Type set resolution template used to construct $(LREF Algebraic) . 366 +/ 367 template TypeSet(T...) 368 { 369 import std.meta: staticSort, staticMap, allSatisfy, anySatisfy; 370 // sort types by sizeof and them mangleof 371 // but typeof(null) goes first 372 static if (is(staticMap!(TryRemoveConst, T) == T)) 373 static if (is(NoDuplicates!T == T)) 374 static if (staticIsSorted!(TypeCmp, T)) 375 { 376 alias TypeSet = T; 377 } 378 else 379 alias TypeSet = .TypeSet!(staticSort!(TypeCmp, T)); 380 else 381 alias TypeSet = TypeSet!(NoDuplicates!T); 382 else 383 alias TypeSet = TypeSet!(staticMap!(TryRemoveConst, T)); 384 } 385 386 // IonNull goes first as well 387 private template isIonNull(T) 388 { 389 static if (is(T == TagInfo!(U, name), U, string name)) 390 enum isIonNull = .isIonNull!U; 391 else 392 enum isIonNull = T.stringof == "IonNull"; 393 } 394 395 private template TypeCmp(A, B) 396 { 397 enum bool TypeCmp = is(A == B) ? false: 398 is(A == typeof(null)) ? true: 399 is(B == typeof(null)) ? false: 400 isIonNull!A ? true: 401 isIonNull!B ? false: 402 is(A == void) || is(A == TagInfo!(void, vaname), string vaname) ? true: 403 is(B == void) || is(A == TagInfo!(void, vbname), string vbname) ? false: 404 A.sizeof < B.sizeof ? true: 405 A.sizeof > B.sizeof ? false: 406 A.mangleof < B.mangleof; 407 } 408 409 /// 410 version(mir_core_test) unittest 411 { 412 static struct S {} 413 alias C = S; 414 alias Int = int; 415 static assert(is(TypeSet!(S, int) == TypeSet!(Int, C))); 416 static assert(is(TypeSet!(S, int, int) == TypeSet!(Int, C))); 417 static assert(!is(TypeSet!(uint, S) == TypeSet!(int, S))); 418 } 419 420 private template applyTags(string[] tagNames, T...) 421 if (tagNames.length == T.length) 422 { 423 import std.meta: AliasSeq; 424 static if (tagNames.length == 0) 425 alias applyTags = AliasSeq!(); 426 else 427 alias applyTags = AliasSeq!(TagInfo!(T[0], tagNames[0]), .applyTags!(tagNames[1 .. $], T[1 .. $])); 428 } 429 430 /++ 431 Checks if the type list is $(LREF TypeSet). 432 +/ 433 enum bool isTypeSet(T...) = is(T == TypeSet!T); 434 435 /// 436 @safe pure version(mir_core_test) unittest 437 { 438 static assert(isTypeSet!(TypeSet!())); 439 static assert(isTypeSet!(TypeSet!void)); 440 static assert(isTypeSet!(TypeSet!(void, int, typeof(null)))); 441 } 442 443 /++ 444 Variant Type (aka Algebraic Type). 445 446 The implementation is defined as 447 ---- 448 alias Variant(T...) = Algebraic!(TypeSet!T); 449 ---- 450 451 Compatible with BetterC mode. 452 +/ 453 alias Variant(T...) = Algebraic!(TypeSet!T); 454 455 /// 456 @safe pure @nogc 457 version(mir_core_test) unittest 458 { 459 Variant!(int, double, string) v = 5; 460 assert(v.get!int == 5); 461 v = 3.14; 462 assert(v == 3.14); 463 // auto x = v.get!long; // won't compile, type long not allowed 464 // v = '1'; // won't compile, type char not allowed 465 } 466 467 /// Single argument Variant 468 // and Type with copy constructor 469 @safe pure nothrow @nogc 470 version(mir_core_test) unittest 471 { 472 static struct S 473 { 474 int n; 475 this(ref return scope inout S rhs) inout 476 { 477 this.n = rhs.n + 1; 478 } 479 } 480 481 Variant!S a = S(); 482 auto b = a; 483 484 import mir.conv; 485 assert(a.get!S.n == 0); 486 assert(b.n == 1); //direct access of a member in case of all algebraic types has this member 487 } 488 489 /// Empty type set 490 @safe pure nothrow @nogc version(mir_core_test) unittest 491 { 492 Variant!() a; 493 auto b = a; 494 assert(a.toHash == 0); 495 assert(a == b); 496 assert(a <= b && b >= a); 497 static assert(typeof(a).sizeof == 1); 498 } 499 500 /// Small types 501 @safe pure nothrow @nogc version(mir_core_test) unittest 502 { 503 static struct S { ubyte d; } 504 static assert(Nullable!(byte, char, S).sizeof == 2); 505 } 506 507 @safe pure nothrow @nogc version(mir_core_test) unittest 508 { 509 static struct S { ubyte[3] d; } 510 static assert(Nullable!(ushort, wchar, S).sizeof == 6); 511 } 512 513 // /// opPostMove support 514 // @safe pure @nogc nothrow 515 // version(mir_core_test) unittest 516 // { 517 // import std.algorithm.mutation: move; 518 519 // static struct S 520 // { 521 // uint s; 522 523 // void opPostMove(const ref S old) nothrow 524 // { 525 // this.s = old.s + 1; 526 // } 527 // } 528 529 // Variant!S a; 530 531 // auto b = a.move; 532 // assert(b.s == 1); 533 // } 534 535 /++ 536 Tagged Variant Type (aka Tagged Algebraic Type). 537 538 Compatible with BetterC mode. 539 540 Template has two declarations: 541 ---- 542 // and 543 template TaggedVariant(T) 544 if (is(T == union)) 545 { 546 ... 547 } 548 ---- 549 550 See_also: $(LREF Variant), $(LREF isTaggedVariant). 551 +/ 552 deprecated ("Use Algebraic!Union instead") 553 template TaggedVariant(T) 554 if (is(T == union)) 555 { 556 alias TaggedVariant = Algebraic!T; 557 } 558 559 /// Json Value with styles 560 @safe pure 561 version(mir_core_test) unittest 562 { 563 enum Style { block, flow } 564 565 static struct SomeMetadata { 566 int a; 567 @safe pure nothrow @nogc scope 568 int opCmp(scope const SomeMetadata rhs) const { return a - rhs.a; } 569 } 570 571 static struct ParsePosition 572 { 573 string file, line, column; 574 575 void toString()(scope ref W w) scope const 576 { 577 w.put(file); 578 if (line) { 579 w.put("("); w.put(line); 580 if (column) { w.put(","); w.put(column); } 581 w.put(")"); 582 } 583 } 584 } 585 586 static union Json_ 587 { 588 typeof(null) null_; 589 bool boolean; 590 long integer; 591 double floating; 592 // Not, that `string` is't builtin type but an alias in `object.d` 593 // So we can use `string` as a name of the string field 594 immutable(char)[] string; 595 This[] array; 596 // commented out to test `opCmp` primitive 597 // This[immutable(char)[]] object; 598 599 @algMeta: 600 bool active; 601 SomeMetadata metadata; 602 @algTransp: 603 Style style; 604 @algVerbose ParsePosition position; 605 } 606 607 alias JsonAlgebraic = Algebraic!Json_; 608 609 // typeof(null) has priority 610 static assert(JsonAlgebraic.Kind.init == JsonAlgebraic.Kind.null_); 611 static assert(JsonAlgebraic.Kind.null_ == 0); 612 613 // Kind and AllowedTypes has the same order 614 static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.array] == JsonAlgebraic[])); 615 static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.boolean] == bool)); 616 static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.floating] == double)); 617 static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.integer] == long)); 618 static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.null_] == typeof(null))); 619 // static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.object] == JsonAlgebraic[string])); 620 621 JsonAlgebraic v; 622 assert(v.kind == JsonAlgebraic.Kind.null_); 623 624 v = 1; 625 assert(v.kind == JsonAlgebraic.Kind.integer); 626 assert(v == 1); 627 v = JsonAlgebraic(1); 628 assert(v == 1); 629 v = v.get!(long, double); 630 631 v = "Tagged!"; 632 // member-based access. Simple! 633 assert(v..string == "Tagged!"); 634 // type-based access 635 assert(v.get!string == "Tagged!"); 636 assert(v.trustedGet!string == "Tagged!"); 637 638 assert(v.kind == JsonAlgebraic.Kind..string); 639 640 assert(v.get!"string" == "Tagged!"); // string-based get 641 assert(v.trustedGet!"string" == "Tagged!"); // string-based trustedGet 642 643 assert(v.get!(JsonAlgebraic.Kind..string) == "Tagged!"); // Kind-based get 644 assert(v.trustedGet!(JsonAlgebraic.Kind..string) == "Tagged!"); // Kind-based trustedGet 645 646 // checks 647 assert(v._is!string); // type based 648 assert(v._is!"string"); // string based 649 assert(v._is!(JsonAlgebraic.Kind..string)); // 650 651 v = null; 652 assert(v.kind == JsonAlgebraic.Kind.null_); 653 654 v = [JsonAlgebraic("str"), JsonAlgebraic(4.3)]; 655 656 assert(v.kind == JsonAlgebraic.Kind.array); 657 assert(v.trustedGet!(JsonAlgebraic[])[1].kind == JsonAlgebraic.Kind.floating); 658 659 JsonAlgebraic w = v; 660 w.style = Style.flow; 661 assert(v.style != w.style); 662 assert(v == w); 663 assert(v <= w); 664 assert(v >= w); 665 assert(v.toHash == w.toHash); 666 w.active = true; 667 assert(v != w); 668 assert(v.toHash != w.toHash); 669 assert(v.get!"array" == w.get!"array"); 670 assert(v < w); 671 672 // test equality with self-referencing allowed type 673 auto arr = [JsonAlgebraic("str"), JsonAlgebraic(120)]; 674 v = arr; 675 assert(v == arr); 676 assert(v == [JsonAlgebraic("str"), JsonAlgebraic(120)]); 677 } 678 679 /// Wrapped algebraic with propogated primitives 680 @safe pure 681 version(mir_core_test) unittest 682 { 683 static struct Response 684 { 685 private union Response_ 686 { 687 double float_; 688 immutable(char)[] string; 689 Response[] array; 690 Response[immutable(char)[]] table; 691 } 692 693 alias ResponseAlgebraic = Algebraic!Response_; 694 695 ResponseAlgebraic data; 696 alias Tag = ResponseAlgebraic.Kind; 697 698 // propogates opEquals, opAssign, and other primitives 699 alias data this; 700 701 static foreach (T; ResponseAlgebraic.AllowedTypes) 702 this(T v) @safe pure nothrow @nogc { data = v; } 703 } 704 705 Response v = 3.0; 706 assert(v.kind == Response.Tag.float_); 707 v = "str"; 708 assert(v == "str"); 709 } 710 711 /++ 712 Nullable $(LREF Variant) Type (aka Algebraic Type). 713 714 The implementation is defined as 715 ---- 716 alias Nullable(T...) = Variant!(typeof(null), T); 717 ---- 718 719 In additional to common algebraic API the following members can be accesssed: 720 $(UL 721 $(LI $(LREF .Algebraic.isNull)) 722 $(LI $(LREF .Algebraic.nullify)) 723 $(LI $(LREF .Algebraic.get.2)) 724 ) 725 726 Compatible with BetterC mode. 727 +/ 728 alias Nullable(T...) = Variant!(typeof(null), T); 729 730 /// ditto 731 Nullable!T nullable(T)(T t) 732 { 733 import core.lifetime: forward; 734 return Nullable!T(forward!t); 735 } 736 737 /++ 738 Single type `Nullable` 739 +/ 740 @safe pure @nogc 741 version(mir_core_test) unittest 742 { 743 static assert(is(Nullable!int == Variant!(typeof(null), int))); 744 745 Nullable!int a = 5; 746 assert(a.get!int == 5); 747 748 a.nullify; 749 assert(a.isNull); 750 751 a = 4; 752 assert(!a.isNull); 753 assert(a.get == 4); 754 assert(a == 4); 755 a = 4; 756 757 a = null; 758 assert(a == null); 759 } 760 761 /// Empty nullable type set support 762 @safe pure nothrow @nogc version(mir_core_test) unittest 763 { 764 Nullable!() a; 765 auto b = a; 766 assert(a.toHash == 0); 767 assert(a == b); 768 assert(a <= b && b >= a); 769 static assert(typeof(a).sizeof == 1); 770 } 771 772 private bool contains(scope const char[][] names, scope const char[] member) 773 @safe pure nothrow @nogc 774 { 775 foreach (name; names) 776 if (name == member) 777 return true; 778 return false; 779 } 780 781 /++ 782 Algebraic implementation. 783 For more portable code, it is higly recommeded to don't use this template directly. 784 Instead, please use of $(LREF Variant) and $(LREF Nullable), which sort types. 785 +/ 786 struct Algebraic(T__...) 787 { 788 import mir.internal.meta: getUDAs; 789 import core.lifetime: moveEmplace; 790 import mir.conv: emplaceRef; 791 import mir.reflection: isPublic, hasField, isProperty; 792 import std.meta: Filter, AliasSeq, ApplyRight, anySatisfy, allSatisfy, staticMap, templateOr, templateNot, templateAnd; 793 import std.traits: 794 hasElaborateAssign, 795 hasElaborateCopyConstructor, 796 hasElaborateDestructor, 797 hasMember, 798 hasUDA, 799 isAggregateType, 800 isAssociativeArray, 801 isDynamicArray, 802 isEqualityComparable, 803 isOrderingComparable, 804 Largest, 805 Unqual 806 ; 807 808 static if (T__.length == 1 && is(T__[0] == union)) 809 { 810 private alias UMTypeInfoOf__(immutable(char)[] member) = TagInfo!( 811 typeof(__traits(getMember, T__[0], member)), 812 member, 813 getUDAs!(T__[0], member, algMeta), 814 getUDAs!(T__[0], member, algTransp), 815 getUDAs!(T__[0], member, algVerbose), 816 ); 817 818 private alias UMGetType__(alias TI) = TI.Type; 819 private enum bool UMGetMeta(alias TI) = TI.meta; 820 821 private alias AllInfo__ = staticMap!(UMTypeInfoOf__, __traits(allMembers, T__[0])); 822 private alias TypesInfo__ = Filter!(templateNot!UMGetMeta, AllInfo__); 823 private alias MetaInfo__ = Filter!(UMGetMeta, AllInfo__); 824 alias Types__ = staticMap!(UMGetType__, TypesInfo__); 825 826 /++ 827 +/ 828 static immutable char[][] metaFieldNames__ = () { 829 immutable(char)[][] ret; 830 foreach (T; MetaInfo__) 831 ret ~= T.tag; 832 return ret; 833 } (); 834 835 /++ 836 +/ 837 static immutable char[][] typeFieldNames__ = () { 838 immutable(char)[][] ret; 839 foreach (T; TypesInfo__) 840 ret ~= T.tag; 841 return ret; 842 } (); 843 } 844 else 845 { 846 alias Types__ = T__; 847 private alias MetaInfo__ = T__[0 .. 0]; 848 enum immutable(char[][]) metaFieldNames__ = null; 849 enum immutable(char[][]) typeFieldNames__ = null; 850 } 851 852 private enum bool variant_test__ = is(Types__ == AliasSeq!(typeof(null), double)); 853 854 /++ 855 Allowed types list 856 See_also: $(LREF TypeSet) 857 +/ 858 alias AllowedTypes = AliasSeq!(ReplaceTypeUnless!(.isVariant, .This, Algebraic!T__, Types__)); 859 860 version(mir_core_test) 861 static if (variant_test__) 862 /// 863 unittest 864 { 865 import std.meta: AliasSeq; 866 867 alias V = Nullable! 868 ( 869 This*, 870 string, 871 double, 872 bool, 873 ); 874 875 static assert(is(V.AllowedTypes == TypeSet!( 876 typeof(null), 877 bool, 878 string, 879 double, 880 V*))); 881 } 882 883 static foreach (i, T; MetaInfo__) 884 mixin ("MetaInfo__[" ~ i.stringof ~ "].Type " ~ T.tag ~";"); 885 886 private alias _Payload = Replace!(void, _Void!(), Replace!(typeof(null), _Null!(), AllowedTypes)); 887 888 private static union Storage__ 889 { 890 _Payload payload; 891 892 static foreach (int i, P; _Payload) 893 mixin(`alias _member_` ~ i.stringof ~ ` = payload[` ~ i.stringof ~ `];`); 894 895 static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null))) || is(AllowedTypes == AliasSeq!void)) 896 ubyte[0] bytes; 897 else 898 ubyte[Largest!_Payload.sizeof] bytes; 899 } 900 901 private Storage__ storage__; 902 903 static if (AllowedTypes.length > 1) 904 { 905 static if ((Storage__.alignof & 1) && _Payload.length <= ubyte.max) 906 private alias ID__ = ubyte; 907 else 908 static if ((Storage__.alignof & 2) && _Payload.length <= ushort.max) 909 private alias ID__ = ushort; 910 else 911 // static if (Storage__.alignof & 3) 912 private alias ID__ = uint; 913 // else 914 // private alias ID__ = ulong; 915 916 ID__ identifier__; 917 } 918 else 919 { 920 private alias ID__ = uint; 921 enum ID__ identifier__ = 0; 922 } 923 924 version (D_Ddoc) 925 { 926 /++ 927 Algebraic Kind. 928 929 Defined as enum for tagged algebraics and as unsigned for common algebraics. 930 931 The Kind enum contains the members defined using tag names. 932 933 If the algebraic type is $(LREF Nullable) then the default Kind enum member has zero value and corresponds to `typeof(null)`. 934 935 See_also: $(LREF TaggedVariant). 936 +/ 937 enum Kind { _not_me_but_tags_name_list_ } 938 } 939 940 static if (typeFieldNames__.length) 941 { 942 version (D_Ddoc){} 943 else 944 { 945 mixin(enumKindText(typeFieldNames__)); 946 947 } 948 } 949 else 950 { 951 version (D_Ddoc){} 952 else 953 { 954 alias Kind = ID__; 955 } 956 } 957 958 /++ 959 Returns: $(LREF .Algebraic.Kind). 960 961 Defined as enum for tagged algebraics and as unsigned for common algebraics. 962 See_also: $(LREF TaggedVariant). 963 +/ 964 Kind kind() const @safe pure nothrow @nogc @property 965 { 966 assert(identifier__ <= Kind.max); 967 return cast(Kind) identifier__; 968 } 969 970 static if (anySatisfy!(hasElaborateDestructor, _Payload)) 971 ~this() @trusted 972 { 973 S: switch (identifier__) 974 { 975 static foreach (i, T; AllowedTypes) 976 static if (hasElaborateDestructor!T) 977 { 978 case i: 979 (*cast(Unqual!(_Payload[i])*)&storage__.payload[i]).__xdtor; 980 break S; 981 } 982 default: 983 } 984 version(mir_secure_memory) 985 storage__.bytes = 0xCC; 986 } 987 988 // static if (anySatisfy!(hasOpPostMove, _Payload)) 989 // void opPostMove(const ref typeof(this) old) 990 // { 991 // S: switch (identifier__) 992 // { 993 // static foreach (i, T; AllowedTypes) 994 // static if (hasOpPostMove!T) 995 // { 996 // case i: 997 // this.storage__.payload[i].opPostMove(old.storage__.payload[i]); 998 // return; 999 // } 1000 // default: return; 1001 // } 1002 // } 1003 1004 static if (AllowedTypes.length) 1005 { 1006 static if (!__traits(compiles, (){ _Payload[0] arg; })) 1007 { 1008 @disable this(); 1009 } 1010 1011 static if (allSatisfy!(isDynamicArray, AllowedTypes)) 1012 { 1013 auto length()() const @property 1014 { 1015 switch (identifier__) 1016 { 1017 static foreach (i, T; AllowedTypes) 1018 { 1019 case i: 1020 return trustedGet!T().length; 1021 } 1022 default: assert(0); 1023 } 1024 } 1025 1026 auto length()(size_t length) @property 1027 { 1028 switch (identifier__) 1029 { 1030 static foreach (i, T; AllowedTypes) 1031 { 1032 case i: 1033 return trustedGet!T().length = length; 1034 } 1035 default: assert(0); 1036 } 1037 } 1038 1039 alias opDollar(size_t pos : 0) = length; 1040 1041 /// Returns: slice type of `Slice!(IotaIterator!size_t)` 1042 size_t[2] opSlice(size_t dimension)(size_t i, size_t j) @safe scope const 1043 if (dimension == 0) 1044 in(i <= j, "Algebraic.opSlice: the left opSlice boundary must be less than or equal to the right bound.") 1045 { 1046 return [i, j]; 1047 } 1048 1049 auto opIndex()(size_t index) 1050 { 1051 return this.visit!(a => a[index]); 1052 } 1053 1054 auto opIndex()(size_t index) const 1055 { 1056 return this.visit!(a => a[index]); 1057 } 1058 1059 auto opIndex()(size_t[2] index) 1060 { 1061 auto ret = this; 1062 S: switch (identifier__) 1063 { 1064 static foreach (i, T; AllowedTypes) 1065 { 1066 case i: 1067 ret.trustedGet!T() = ret.trustedGet!T()[index[0] .. index[1]]; 1068 break S; 1069 } 1070 default: assert(0); 1071 } 1072 return ret; 1073 } 1074 1075 auto opIndexAssign(T)(T value, size_t index) 1076 { 1077 return this.tryMatch!((ref array, ref value) => array[index] = value)(value); 1078 } 1079 } 1080 } 1081 1082 /// Construct an algebraic type from its subset. 1083 this(RhsTypes...)(Algebraic!RhsTypes rhs) 1084 if (!(hasElaborateCopyConstructor!(Algebraic!T__) && is(Algebraic!RhsTypes == typeof(this))) && allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes)) 1085 { 1086 import core.lifetime: move; 1087 static if (is(RhsTypes == Types__)) 1088 this = move(rhs); 1089 else 1090 { 1091 static foreach (member; metaFieldNames__) 1092 static if (Algebraic!RhsTypes.metaFieldNames__.contains(member)) 1093 __traits(getMember, this, member) = move(__traits(getMember, rhs, member)); 1094 1095 switch (rhs.identifier__) 1096 { 1097 static foreach (i, T; Algebraic!RhsTypes.AllowedTypes) 1098 { 1099 case i: 1100 static if (__traits(compiles, __ctor(move(rhs.trustedGet!T)))) 1101 __ctor(move(rhs.trustedGet!T)); 1102 else 1103 __ctor(rhs.trustedGet!T); 1104 return; 1105 } 1106 default: 1107 assert(0, variantMemberExceptionMsg); 1108 } 1109 } 1110 } 1111 1112 version(mir_core_test) 1113 static if (variant_test__) 1114 /// 1115 unittest 1116 { 1117 alias Float = Variant!(float, double); 1118 alias Int = Variant!(long, int); 1119 alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes); 1120 1121 Float fp = 3.0; 1122 Number number = fp; // constructor call 1123 assert(number == 3.0); 1124 1125 Int integer = 12L; 1126 number = Number(integer); 1127 assert(number == 12L); 1128 } 1129 1130 static if (!allSatisfy!(isCopyable, AllowedTypes)) 1131 @disable this(this); 1132 else 1133 static if (anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) 1134 { 1135 import std.meta: Filter; 1136 private alias CC_AllowedTypes = Filter!(hasElaborateCopyConstructor, AllowedTypes); 1137 // private enum _allCanImplicitlyRemoveConst = allSatisfy!(canImplicitlyRemoveConst, AllowedTypes); 1138 // private enum _allCanRemoveConst = allSatisfy!(canRemoveConst, AllowedTypes); 1139 // private enum _allHaveImplicitSemiMutableConstruction = _allCanImplicitlyRemoveConst && _allHaveMutableConstruction; 1140 1141 static if (__VERSION__ < 2094) 1142 private static union _StorageI(uint i) 1143 { 1144 _Payload[i] payload; 1145 ubyte[Storage__.bytes.length] bytes; 1146 } 1147 1148 static if (allSatisfy!(hasInoutConstruction, CC_AllowedTypes)) 1149 this(return ref scope inout Algebraic rhs) inout 1150 { 1151 static foreach (member; metaFieldNames__) 1152 __traits(getMember, this, member) = __traits(getMember, rhs, member); 1153 1154 static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__; 1155 static foreach (int i, T; AllowedTypes) 1156 static if (!is(T == typeof(null)) && !is(T == void)) 1157 { 1158 if (identifier__ == i) 1159 { 1160 static if (__VERSION__ < 2094) 1161 { 1162 storage__.bytes = () inout @trusted { 1163 auto ret = inout _StorageI!i(rhs.trustedGet!T); 1164 return ret.bytes; 1165 } (); 1166 return; 1167 } 1168 else 1169 { 1170 storage__ = () inout @trusted { 1171 mixin(`inout Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); 1172 return ret; 1173 } (); 1174 return; 1175 } 1176 } 1177 } 1178 } 1179 else 1180 { 1181 static if (allSatisfy!(hasMutableConstruction, CC_AllowedTypes)) 1182 this(return ref scope Algebraic rhs) 1183 { 1184 static foreach (member; metaFieldNames__) 1185 __traits(getMember, this, member) = __traits(getMember, rhs, member); 1186 1187 static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__; 1188 static foreach (int i, T; AllowedTypes) 1189 static if (!is(T == typeof(null)) && !is(T == void)) 1190 { 1191 if (identifier__ == i) 1192 { 1193 storage__ = () { 1194 mixin(`Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); 1195 return ret; 1196 } (); 1197 return; 1198 } 1199 } 1200 } 1201 1202 static if (allSatisfy!(hasConstConstruction, CC_AllowedTypes)) 1203 this(return ref scope const Algebraic rhs) const 1204 { 1205 static foreach (member; metaFieldNames__) 1206 __traits(getMember, this, member) = __traits(getMember, rhs, member); 1207 1208 static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__; 1209 static foreach (int i, T; AllowedTypes) 1210 static if (!is(T == typeof(null)) && !is(T == void)) 1211 { 1212 if (identifier__ == i) 1213 { 1214 storage__ = () const { 1215 mixin(`const Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); 1216 return ret; 1217 } (); 1218 return; 1219 } 1220 } 1221 } 1222 1223 static if (allSatisfy!(hasImmutableConstruction, CC_AllowedTypes)) 1224 this(return ref scope immutable Algebraic rhs) immutable 1225 { 1226 static foreach (member; metaFieldNames__) 1227 __traits(getMember, this, member) = __traits(getMember, rhs, member); 1228 1229 static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__; 1230 static foreach (int i, T; AllowedTypes) 1231 static if (!is(T == typeof(null)) && !is(T == void)) 1232 { 1233 if (identifier__ == i) 1234 { 1235 storage__ = () immutable { 1236 mixin(`immutable Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); 1237 return ret; 1238 } (); 1239 return; 1240 } 1241 } 1242 } 1243 1244 static if (allSatisfy!(hasSemiImmutableConstruction, CC_AllowedTypes)) 1245 this(return ref scope const Algebraic rhs) immutable 1246 { 1247 static foreach (member; metaFieldNames__) 1248 __traits(getMember, this, member) = __traits(getMember, rhs, member); 1249 1250 static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__; 1251 static foreach (int i, T; AllowedTypes) 1252 static if (!is(T == typeof(null)) && !is(T == void)) 1253 { 1254 if (identifier__ == i) 1255 { 1256 storage__ = () const { 1257 mixin(`immutable Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); 1258 return ret; 1259 } (); 1260 return; 1261 } 1262 } 1263 } 1264 1265 static if (allSatisfy!(hasSemiMutableConstruction, CC_AllowedTypes)) 1266 this(return ref scope const Algebraic rhs) 1267 { 1268 static foreach (member; metaFieldNames__) 1269 __traits(getMember, this, member) = __traits(getMember, rhs, member); 1270 1271 static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__; 1272 static foreach (int i, T; AllowedTypes) 1273 static if (!is(T == typeof(null)) && !is(T == void)) 1274 { 1275 if (identifier__ == i) 1276 { 1277 storage__ = () const { 1278 mixin(`const Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); 1279 return ret; 1280 } (); 1281 return; 1282 } 1283 } 1284 } 1285 } 1286 } 1287 1288 /++ 1289 +/ 1290 static if (typeFieldNames__.length) 1291 size_t toHash() scope @trusted const pure nothrow @nogc 1292 { 1293 size_t hash; 1294 1295 1296 static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null)))) 1297 { 1298 } 1299 else{S: 1300 switch (identifier__) 1301 { 1302 import std.traits: isArray; 1303 static foreach (i, T; AllowedTypes) 1304 { 1305 case i: { 1306 static if (is(T == void)) 1307 hash = i; 1308 else 1309 static if (is(T == typeof(null))) 1310 hash = i; 1311 else 1312 static if (typeFieldNames__.length) // force for tagged types 1313 { 1314 static if (__traits(hasMember, T, "toHash")) 1315 hash = trustedGet!T.toHash; 1316 else 1317 static if (isArray!T) 1318 foreach (ref e; trustedGet!T) 1319 static if (__traits(hasMember, typeof(e), "toHash")) 1320 hash = hashOf(e.toHash, hash); 1321 else 1322 hash = hashOf(e, hash); 1323 else 1324 hash = hashOf(trustedGet!T); 1325 } 1326 else 1327 static if (__traits(compiles, hashOf(trustedGet!T.hashOf, i ^ hash))) 1328 hash = hashOf(trustedGet!T.hashOf, i ^ hash); 1329 else 1330 { 1331 debug pragma(msg, "Mir warning: couldn't compute hash. Expected a `size_t toHash() scope @safe const pure nothrow @nogc` method for " ~ T.stringof); 1332 hash = i; 1333 } 1334 break S; 1335 } 1336 } 1337 default: assert(0); 1338 }} 1339 1340 static foreach (i, T; MetaInfo__) 1341 static if (!T.transparent) 1342 { 1343 static if (is(MetaFieldsTypes[i] == class) || is(MetaFieldsTypes[i] == interface)) 1344 {{ 1345 scope eqfun = delegate() { 1346 hash = hashOf(__traits(getMember, this, T.tag), hash); 1347 }; 1348 trustedAllAttr(eqfun)(); 1349 }} 1350 else 1351 hash = hashOf(__traits(getMember, this, T.tag), hash); 1352 } 1353 return hash; 1354 } 1355 else 1356 size_t toHash() const scope @trusted nothrow 1357 { 1358 size_t hash; 1359 1360 1361 static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null)))) 1362 { 1363 } 1364 else{S: 1365 switch (identifier__) 1366 { 1367 import std.traits: isArray; 1368 static foreach (i, T; AllowedTypes) 1369 { 1370 case i: { 1371 static if (is(T == void)) 1372 hash = i; 1373 else 1374 static if (is(T == typeof(null))) 1375 hash = i; 1376 else 1377 static if (typeFieldNames__.length) // force for tagged types 1378 { 1379 static if (__traits(hasMember, T, "toHash")) 1380 hash = trustedGet!T.toHash; 1381 else 1382 static if (isArray!T) 1383 foreach (ref e; trustedGet!T) 1384 static if (__traits(hasMember, typeof(e), "toHash")) 1385 hash = hashOf(e.toHash, hash); 1386 else 1387 hash = hashOf(e, hash); 1388 else 1389 hash = hashOf(trustedGet!T); 1390 } 1391 else 1392 static if (__traits(compiles, hashOf(trustedGet!T.hashOf, i ^ hash))) 1393 hash = hashOf(trustedGet!T.hashOf, i ^ hash); 1394 else 1395 { 1396 debug pragma(msg, "Mir warning: can't compute hash. Expexted `size_t toHash() scope @safe const pure nothrow @nogc` method for " ~ T.stringof); 1397 hash = i; 1398 } 1399 break S; 1400 } 1401 } 1402 default: assert(0); 1403 }} 1404 1405 static foreach (i, T; MetaInfo__) 1406 static if (!T.transparent) 1407 { 1408 static if (is(MetaFieldsTypes[i] == class) || is(MetaFieldsTypes[i] == interface)) 1409 {{ 1410 scope eqfun = delegate() { 1411 hash = hashOf(__traits(getMember, this, T.tag), hash); 1412 }; 1413 trustedAllAttr(eqfun)(); 1414 }} 1415 else 1416 hash = hashOf(__traits(getMember, this, T.tag), hash); 1417 } 1418 return hash; 1419 } 1420 1421 1422 /// 1423 bool opEquals()(scope const Algebraic rhs) scope @trusted const pure nothrow @nogc 1424 { 1425 return opEquals(rhs); 1426 } 1427 1428 /// ditto 1429 bool opEquals()(scope ref const Algebraic rhs) scope @trusted const pure nothrow @nogc 1430 { 1431 static foreach (i, T; MetaInfo__) 1432 static if (!T.transparent) 1433 { 1434 static if (is(MetaFieldsTypes[i] == class) || is(MetaFieldsTypes[i] == interface)) 1435 {{ 1436 scope eqfun = delegate() { 1437 return __traits(getMember, this, T.tag) != __traits(getMember, rhs, T.tag); 1438 }; 1439 if (trustedAllAttr(eqfun)()) 1440 return false; 1441 }} 1442 else 1443 if (__traits(getMember, this, T.tag) != __traits(getMember, rhs, T.tag)) 1444 return false; 1445 } 1446 1447 static if (AllowedTypes.length == 0) 1448 { 1449 return true; 1450 } 1451 else 1452 { 1453 if (this.identifier__ != rhs.identifier__) 1454 return false; 1455 switch (identifier__) 1456 { 1457 static foreach (i, T; AllowedTypes) 1458 { 1459 case i: 1460 static if (is(T == void)) 1461 return rhs._is!void; 1462 else 1463 static if (is(T == class) || is(T == interface)) 1464 {{ 1465 scope eqfun = delegate() { 1466 return this.trustedGet!T == rhs.trustedGet!T; 1467 }; 1468 return trustedAllAttr(eqfun)(); 1469 }} 1470 else 1471 static if (__traits(isFloating, T)) 1472 return this.trustedGet!T == rhs.trustedGet!T || (this.trustedGet!T != this.trustedGet!T && rhs.trustedGet!T != rhs.trustedGet!T); 1473 else 1474 return this.trustedGet!T == rhs.trustedGet!T; 1475 } 1476 default: assert(0); 1477 } 1478 } 1479 } 1480 1481 /++ 1482 +/ 1483 static if (!anySatisfy!(templateOr!(isAssociativeArray, templateAnd!(isAggregateType, templateNot!hasOpCmp)), staticMap!(basicElementType, AllowedTypes))) 1484 { 1485 static if (typeFieldNames__.length) 1486 int opCmp()(auto ref scope const typeof(this) rhs) scope @trusted const pure nothrow @nogc 1487 { 1488 static foreach (i, T; MetaInfo__) 1489 static if (!T.transparent) 1490 { 1491 static if (__traits(compiles, __cmp(__traits(getMember, this, T.tag), __traits(getMember, rhs, T.tag)))) 1492 { 1493 if (auto d = __cmp(__traits(getMember, this, T.tag), __traits(getMember, rhs, T.tag))) 1494 return d; 1495 } 1496 else 1497 static if (__traits(hasMember, __traits(getMember, this, T.tag), "opCmp") && !is(MetaFieldsTypes[i] == U*, U)) 1498 { 1499 if (auto d = __traits(getMember, this, T.tag).opCmp(__traits(getMember, rhs, T.tag))) 1500 return d; 1501 } 1502 else 1503 { 1504 if (auto d = __traits(getMember, this, T.tag) < __traits(getMember, rhs, T.tag) ? -1 : __traits(getMember, this, T.tag) > __traits(getMember, rhs, T.tag) ? +1 : 0) 1505 return d; 1506 } 1507 } 1508 1509 1510 static if (AllowedTypes.length == 0) 1511 { 1512 return 0; 1513 } 1514 else 1515 { 1516 import std.traits: isArray; 1517 if (auto d = int(this.identifier__) - int(rhs.identifier__)) 1518 return d; 1519 import std.traits: isArray, isPointer; 1520 switch (identifier__) 1521 { 1522 static foreach (i, T; AllowedTypes) 1523 { 1524 case i: 1525 static if (__traits(hasMember, T, "opCmp") && !isPointer!T) 1526 {{ 1527 auto ret = this.trustedGet!T.opCmp(rhs.trustedGet!T); 1528 static if (is(typeof(ret) == int)) 1529 return ret; 1530 else 1531 return ret < 0 ? -1 : ret > 0 ? 1 : 0; 1532 }} 1533 else 1534 static if (!isArray!T) 1535 return this.trustedGet!T < rhs.trustedGet!T ? -1 : 1536 this.trustedGet!T > rhs.trustedGet!T ? +1 : 0; 1537 else 1538 return __cmp(trustedGet!T, rhs.trustedGet!T); 1539 } 1540 default: assert(0); 1541 } 1542 } 1543 } 1544 else 1545 int opCmp()(auto ref scope const typeof(this) rhs) scope @trusted const //pure nothrow @nogc 1546 { 1547 static foreach (i, T; MetaInfo__) 1548 static if (!T.transparent) 1549 { 1550 static if (__traits(compiles, __cmp(__traits(getMember, this, T.tag), __traits(getMember, rhs, T.tag)))) 1551 { 1552 if (auto d = __cmp(__traits(getMember, this, T.tag), __traits(getMember, rhs, T.tag))) 1553 return d; 1554 } 1555 else 1556 static if (__traits(hasMember, __traits(getMember, this, T.tag), "opCmp") && !is(MetaFieldsTypes[i] == U*, U)) 1557 { 1558 if (auto d = __traits(getMember, this, T.tag).opCmp(__traits(getMember, rhs, T.tag))) 1559 return d; 1560 } 1561 else 1562 { 1563 if (auto d = __traits(getMember, this, T.tag) < __traits(getMember, rhs, T.tag) ? -1 : __traits(getMember, this, T.tag) > __traits(getMember, rhs, T.tag) ? +1 : 0) 1564 return d; 1565 } 1566 } 1567 1568 1569 static if (AllowedTypes.length == 0) 1570 { 1571 return 0; 1572 } 1573 else 1574 { 1575 import std.traits: isArray; 1576 if (auto d = int(this.identifier__) - int(rhs.identifier__)) 1577 return d; 1578 import std.traits: isArray, isPointer; 1579 switch (identifier__) 1580 { 1581 static foreach (i, T; AllowedTypes) 1582 { 1583 case i: 1584 static if (__traits(hasMember, T, "opCmp") && !isPointer!T) 1585 {{ 1586 auto ret = this.trustedGet!T.opCmp(rhs.trustedGet!T); 1587 static if (is(typeof(ret) == int)) 1588 return ret; 1589 else 1590 return ret < 0 ? -1 : ret > 0 ? 1 : 0; 1591 }} 1592 else 1593 static if (!isArray!T) 1594 return this.trustedGet!T < rhs.trustedGet!T ? -1 : 1595 this.trustedGet!T > rhs.trustedGet!T ? +1 : 0; 1596 else 1597 return __cmp(trustedGet!T, rhs.trustedGet!T); 1598 } 1599 default: assert(0); 1600 } 1601 } 1602 } 1603 } 1604 1605 /// Requires mir-algorithm package 1606 immutable(char)[] toString()() @trusted scope const 1607 { 1608 static if (AllowedTypes.length == 0) 1609 { 1610 return "Algebraic"; 1611 } 1612 else 1613 { 1614 import mir.conv: to; 1615 immutable(char)[] ret; 1616 static foreach (i, member; metaFieldNames__) 1617 static if (!MetaInfo__[i].transparent) 1618 { 1619 static if (__traits(compiles, { auto s = to!(immutable(char)[])(__traits(getMember, this, member));})) 1620 // should be passed by value to workaround compiler bug 1621 ret ~= to!(immutable(char)[])(__traits(getMember, this, member)); 1622 else 1623 ret ~= AllowedTypes[i].stringof; 1624 ret ~= "::"; 1625 } 1626 switch (identifier__) 1627 { 1628 static foreach (i, T; AllowedTypes) 1629 { 1630 case i: 1631 static if (is(T == void)) 1632 ret ~= "void"; 1633 else 1634 static if (is(T == typeof(null))) 1635 ret ~= "null"; 1636 else 1637 static if (__traits(compiles, { auto s = to!(immutable(char)[])(trustedGet!T);})) 1638 // should be passed by value to workaround compiler bug 1639 ret ~= to!(immutable(char)[])(trustedGet!T); 1640 else 1641 ret ~= AllowedTypes[i].stringof; 1642 return ret; 1643 } 1644 default: assert(0); 1645 } 1646 } 1647 } 1648 1649 ///ditto 1650 void toString(W)(ref scope W w) scope const @trusted pure 1651 if (__traits(compiles, ()pure{ w.put("Algebraic"); })) 1652 { 1653 if (false) 1654 return w.put("Algebraic"); 1655 static if (AllowedTypes.length == 0) 1656 { 1657 return w.put("Algebraic"); 1658 } 1659 else 1660 { 1661 import mir.format: print; 1662 static foreach (i, member; metaFieldNames__) 1663 static if (!MetaInfo__[i].transparent) 1664 { 1665 static if (__traits(compiles, { import mir.format: print; print(w, __traits(getMember, this, member)); })) 1666 { import mir.format: print; print(w, __traits(getMember, this, member)); } 1667 else 1668 w.put(AllowedTypes[i].stringof); 1669 w.put("::"); 1670 } 1671 switch (identifier__) 1672 { 1673 static foreach (i, T; AllowedTypes) 1674 { 1675 case i: 1676 static if (is(T == void)) 1677 w.put("void"); 1678 else 1679 static if (is(T == typeof(null))) 1680 w.put("null"); 1681 else 1682 static if (__traits(compiles, { import mir.format: print; print(w, trustedGet!T); })) 1683 toStringImpl!T(w); 1684 else 1685 w.put(AllowedTypes[i].stringof); 1686 return; 1687 } 1688 default: assert(0); 1689 } 1690 } 1691 } 1692 1693 ///ditto 1694 void toString(W)(ref scope W w) scope const @trusted 1695 if (!__traits(compiles, ()pure{ w.put("Algebraic"); })) 1696 { 1697 if (false) 1698 return w.put("Algebraic"); 1699 static if (AllowedTypes.length == 0) 1700 { 1701 return w.put("Algebraic"); 1702 } 1703 else 1704 { 1705 switch (identifier__) 1706 { 1707 static foreach (i, T; AllowedTypes) 1708 { 1709 case i: 1710 static if (is(T == void)) 1711 return w.put("void"); 1712 else 1713 static if (is(T == typeof(null))) 1714 return w.put("null"); 1715 else 1716 static if (__traits(compiles, { import mir.format: print; print(w, trustedGet!T); })) 1717 return toStringImpl!T(w); 1718 else 1719 return w.put(AllowedTypes[i].stringof); 1720 } 1721 default: assert(0); 1722 } 1723 } 1724 } 1725 1726 private void toStringImpl(T, W)(ref scope W w) @safe scope const pure nothrow @nogc 1727 { 1728 import mir.format: print; 1729 scope pfun = delegate() { 1730 print(w, trustedGet!T); 1731 }; 1732 trustedAllAttr(pfun)(); 1733 } 1734 1735 static if (is(AllowedTypes[0] == typeof(null))) 1736 { 1737 /// 1738 bool opCast(C)() const 1739 if (is(C == bool)) 1740 { 1741 return identifier__ != 0; 1742 } 1743 1744 /// 1745 Algebraic opCast(C)() const 1746 if (is(C == Algebraic)) 1747 { 1748 return this; 1749 } 1750 1751 /// Defined if the first type is `typeof(null)` 1752 bool isNull() const @property { return identifier__ == 0; } 1753 /// ditto 1754 void nullify() { this = null; } 1755 1756 /// ditto 1757 auto get()() 1758 if (allSatisfy!(isCopyable, AllowedTypes[1 .. $]) && AllowedTypes.length != 2 && is(AllowedTypes[0] == typeof(null))) 1759 { 1760 import mir.utility: _expect; 1761 if (_expect(!identifier__, false)) 1762 { 1763 throw variantNullException.toMutable; 1764 } 1765 static if (AllowedTypes.length != 2) 1766 { 1767 Algebraic!(AllowedTypes[1 .. $]) ret; 1768 S: switch (identifier__) 1769 { 1770 static foreach (i, T; AllowedTypes[1 .. $]) 1771 { 1772 { 1773 case i + 1: 1774 if (!hasElaborateCopyConstructor!T && !__ctfe) 1775 goto default; 1776 static if (is(T == void)) 1777 ret = ret._void; 1778 else 1779 ret = this.trustedGet!T; 1780 break S; 1781 } 1782 } 1783 default: 1784 ret.storage__.bytes = this.storage__.bytes; 1785 static if (ret.AllowedTypes.length > 1) 1786 ret.identifier__ = cast(typeof(ret.identifier__))(this.identifier__ - 1); 1787 } 1788 return ret; 1789 } 1790 } 1791 1792 static if (AllowedTypes.length == 2) 1793 { 1794 /++ 1795 Gets the value if not null. If `this` is in the null state, and the optional 1796 parameter `fallback` was provided, it will be returned. Without `fallback`, 1797 calling `get` with a null state is invalid. 1798 1799 When the fallback type is different from the Nullable type, `get(T)` returns 1800 the common type. 1801 1802 Params: 1803 fallback = the value to return in case the `Nullable` is null. 1804 1805 Returns: 1806 The value held internally by this `Nullable`. 1807 +/ 1808 auto ref inout(AllowedTypes[1]) get() return inout 1809 { 1810 assert(identifier__, "Called `get' on null Nullable!(" ~ AllowedTypes[1].stringof ~ ")."); 1811 return trustedGet!(AllowedTypes[1]); 1812 } 1813 1814 version(mir_core_test) 1815 static if (variant_test__) 1816 /// 1817 @safe pure nothrow @nogc 1818 unittest 1819 { 1820 enum E { a = "a", b = "b" } 1821 Nullable!E f = E.a; 1822 auto e = f.get(); 1823 static assert(is(typeof(e) == E), Nullable!E.AllowedTypes.stringof); 1824 assert(e == E.a); 1825 1826 assert(f.get(E.b) == E.a); 1827 1828 f = null; 1829 assert(f.get(E.b) == E.b); 1830 } 1831 1832 /// ditto 1833 @property auto ref inout(AllowedTypes[1]) get()(auto ref inout(AllowedTypes[1]) fallback) return inout 1834 { 1835 return isNull ? fallback : get(); 1836 } 1837 } 1838 } 1839 1840 /++ 1841 Checks if the underlaying type is an element of a user provided type set. 1842 +/ 1843 bool _is(R : Algebraic!RetTypes, RetTypes...)() @safe pure nothrow @nogc const @property 1844 if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes)) 1845 { 1846 static if (is(RetTypes == Types__)) 1847 return true; 1848 else 1849 { 1850 import std.meta: staticIndexOf; 1851 import std.traits: CopyTypeQualifiers; 1852 alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes; 1853 alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes); 1854 // uint rhsTypeId; 1855 switch (identifier__) 1856 { 1857 foreach (i, T; AllowedTypes) 1858 static if (staticIndexOf!(T, RhsAllowedTypes) >= 0) 1859 { 1860 case i: 1861 return true; 1862 } 1863 default: 1864 return false; 1865 } 1866 } 1867 } 1868 1869 /// ditto 1870 bool _is(RetTypes...)() @safe pure nothrow @nogc const @property 1871 if (RetTypes.length > 1) 1872 { 1873 return this._is!(Variant!RetTypes); 1874 } 1875 1876 /++ 1877 `nothrow` $(LREF .Algebraic.get) alternative that returns an algebraic subset. 1878 +/ 1879 auto ref trustedGet(R : Algebraic!RetTypes, this This, RetTypes...)() return @property 1880 if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes)) 1881 { 1882 static if (is(RetTypes == Types__)) 1883 return this; 1884 else 1885 { 1886 import std.meta: staticIndexOf; 1887 import std.traits: CopyTypeQualifiers; 1888 alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes; 1889 alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes); 1890 // uint rhsTypeId; 1891 switch (identifier__) 1892 { 1893 foreach (i, T; AllowedTypes) 1894 static if (staticIndexOf!(T, RhsAllowedTypes) >= 0) 1895 { 1896 case i: 1897 static if (is(T == void)) 1898 return (()@trusted => cast(Ret) Ret._void)(); 1899 else 1900 return Ret(trustedGet!T); 1901 } 1902 default: 1903 assert(0, variantMemberExceptionMsg); 1904 } 1905 } 1906 } 1907 1908 /// ditto 1909 template trustedGet(RetTypes...) 1910 if (RetTypes.length > 1) 1911 { 1912 /// 1913 auto ref trustedGet(this This)() return 1914 { 1915 return this.trustedGet!(Variant!RetTypes); 1916 } 1917 } 1918 1919 version(mir_core_test) 1920 static if (variant_test__) 1921 /// 1922 @safe pure nothrow @nogc 1923 unittest 1924 { 1925 alias Float = Variant!(float, double); 1926 alias Int = Variant!(long, int); 1927 alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes); 1928 1929 Number number = 3.0; 1930 assert(number._is!Float); 1931 auto fp = number.trustedGet!Float; 1932 static assert(is(typeof(fp) == Float)); 1933 assert(fp == 3.0); 1934 1935 // type list overload 1936 number = 12L; 1937 assert(number._is!(int, long)); 1938 auto integer = number.trustedGet!(int, long); 1939 static assert(is(typeof(integer) == Int)); 1940 assert(integer == 12L); 1941 } 1942 1943 static if (typeFieldNames__.length) 1944 { 1945 /// `trustedGet` overload that accept $(LREF .Algebraic.Kind). 1946 alias trustedGet(Kind kind) = trustedGet!(AllowedTypes[kind]); 1947 /// ditto 1948 alias trustedGet(immutable(char)[] kind) = trustedGet!(__traits(getMember, Kind, kind)); 1949 } 1950 1951 /++ 1952 Gets an algebraic subset. 1953 1954 Throws: Exception if the storage contains value of the type that isn't represented in the allowed type set of the requested algebraic. 1955 +/ 1956 auto ref get(R : Algebraic!RetTypes, this This, RetTypes...)() return @property 1957 if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes)) 1958 { 1959 static if (is(RetTypes == Types__)) 1960 return this; 1961 else 1962 { 1963 import std.meta: staticIndexOf; 1964 import std.traits: CopyTypeQualifiers; 1965 alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes; 1966 alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes); 1967 // uint rhsTypeId; 1968 switch (identifier__) 1969 { 1970 foreach (i, T; AllowedTypes) 1971 static if (staticIndexOf!(T, RhsAllowedTypes) >= 0) 1972 { 1973 case i: 1974 static if (is(T == void)) 1975 return (()@trusted => cast(Ret) Ret._void)(); 1976 else 1977 return Ret(trustedGet!T); 1978 } 1979 default: 1980 throw variantMemberException.toMutable; 1981 } 1982 } 1983 } 1984 1985 /// ditto 1986 template get(RetTypes...) 1987 if (RetTypes.length > 1) 1988 { 1989 /// 1990 auto ref get(this This)() return 1991 { 1992 return this.get!(Variant!RetTypes); 1993 } 1994 } 1995 1996 version(mir_core_test) 1997 static if (variant_test__) 1998 /// 1999 @safe pure @nogc 2000 unittest 2001 { 2002 alias Float = Variant!(float, double); 2003 alias Int = Variant!(long, int); 2004 alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes); 2005 2006 Number number = 3.0; 2007 auto fp = number.get!Float; 2008 static assert(is(typeof(fp) == Float)); 2009 assert(fp == 3.0); 2010 2011 // type list overload 2012 number = 12L; 2013 auto integer = number.get!(int, long); 2014 static assert(is(typeof(integer) == Int)); 2015 assert(integer == 12L); 2016 } 2017 2018 static if (typeFieldNames__.length) 2019 { 2020 /// `get` overload that accept $(LREF .Algebraic.Kind). 2021 alias get(Kind kind) = get!(AllowedTypes[kind]); 2022 /// ditto 2023 alias get(immutable(char)[] kind) = get!(__traits(getMember, Kind, kind)); 2024 2025 /// `_is` overload that accept $(LREF .Algebraic.Kind). 2026 alias _is(Kind kind) = _is!(AllowedTypes[kind]); 2027 /// ditto 2028 alias _is(immutable(char)[] kind) = _is!(__traits(getMember, Kind, kind)); 2029 2030 static foreach (member; typeFieldNames__) 2031 mixin ("alias " ~ member ~ `() = get!"` ~ member ~ `";`); 2032 } 2033 2034 private alias _ReflectionTypes = AllowedTypes[is(AllowedTypes[0] == typeof(null)) .. $]; 2035 2036 static if (_ReflectionTypes.length) 2037 this(this This, Args...)(auto ref Args args) 2038 if (Args.length && (Args.length > 1 || !isLikeVariant!(Args[0]))) 2039 { 2040 import std.traits: CopyTypeQualifiers; 2041 import core.lifetime: forward; 2042 2043 template CanCompile(T) 2044 { 2045 alias Q = CopyTypeQualifiers!(This, T); 2046 enum CanCompile = __traits(compiles, new Q(forward!args)); 2047 } 2048 2049 alias TargetType = Filter!(CanCompile, _ReflectionTypes); 2050 static if (TargetType.length == 0) 2051 static assert(0, typeof(this).stringof ~ ".this: no types can be constructed with arguments " ~ Args.stringof); 2052 static assert(TargetType.length == 1, typeof(this).stringof ~ ".this: multiple types " ~ TargetType.stringof ~ " can be constructed with arguments " ~ Args.stringof); 2053 alias TT = TargetType[0]; 2054 static if (is(TT == struct) || is(TT == union)) 2055 this(CopyTypeQualifiers!(This, TT)(forward!args)); 2056 else 2057 this(new CopyTypeQualifiers!(This, TT)(forward!args)); 2058 } 2059 2060 static if (_ReflectionTypes.length && allSatisfy!(isSimpleAggregateType, _ReflectionTypes)) 2061 { 2062 static foreach (member; AllMembersRec!(_ReflectionTypes[0])) 2063 static if ( 2064 !.algebraicMembers.contains(member) && 2065 !metaFieldNames__.contains(member) && 2066 !typeFieldNames__.contains(member) && 2067 !(member.length >= 2 && (member[0 .. 2] == "__" || member[$ - 2 .. $] == "__"))) 2068 static if (allSatisfy!(ApplyRight!(hasMember, member), _ReflectionTypes)) 2069 static if (!anySatisfy!(ApplyRight!(isMemberType, member), _ReflectionTypes)) 2070 static if (allSatisfy!(ApplyRight!(isSingleMember, member), _ReflectionTypes)) 2071 static if (allSatisfy!(ApplyRight!(isPublic, member), _ReflectionTypes)) 2072 { 2073 static if (allSatisfy!(ApplyRight!(hasField, member), _ReflectionTypes) && NoDuplicates!(staticMap!(ApplyRight!(memberTypeOf, member), _ReflectionTypes)).length == 1) 2074 { 2075 mixin(`ref ` ~ member ~q{()() inout return @trusted pure nothrow @nogc @property { return this.getMember!member; }}); 2076 } 2077 else 2078 static if (allSatisfy!(ApplyRight!(templateOr!(hasField, isProperty), member), _ReflectionTypes)) 2079 { 2080 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; }}); 2081 } 2082 static if (allSatisfy!(ApplyRight!(templateNot!(templateOr!(hasField, isProperty)), member), _ReflectionTypes)) 2083 { 2084 mixin(`template ` ~ member ~`(TArgs...) { auto ref ` ~ member ~q{(this This, Args...)(auto ref Args args) { static if (args.length) { import core.lifetime: forward; return this.getMember!(member, TArgs)(forward!args); } else return this.getMember!(member, TArgs); }} ~ `}`); 2085 } 2086 } 2087 } 2088 2089 /// 2090 ref opAssign(RhsTypes...)(Algebraic!RhsTypes rhs) return @trusted 2091 if (allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes) && !is(Algebraic == Algebraic!RhsTypes)) 2092 { 2093 import core.lifetime: forward; 2094 this = this.init; 2095 __ctor(forward!rhs); 2096 return this; 2097 } 2098 2099 // pragma(msg, AllowedTypes); 2100 2101 static foreach (int i, T; AllowedTypes) 2102 { 2103 /// Zero cost always nothrow `get` alternative 2104 auto ref trustedGet(E)() @trusted @property return inout nothrow 2105 if (is(E == T)) 2106 { 2107 assert (i == identifier__); 2108 static if (is(T == typeof(null))) 2109 return null; 2110 else 2111 static if (is(T == void)) 2112 return; 2113 else 2114 return storage__.payload[i]; 2115 } 2116 2117 /++ 2118 Throws: Exception if the storage contains value of other type 2119 +/ 2120 auto ref get(E)() @property return inout 2121 if (is(E == T)) 2122 { 2123 import mir.utility: _expect; 2124 static if (AllowedTypes.length > 1) 2125 { 2126 if (_expect(i != identifier__, false)) 2127 { 2128 throw variantException.toMutable; 2129 } 2130 } 2131 return trustedGet!T; 2132 } 2133 2134 /++ 2135 Checks if the storage stores an allowed type. 2136 +/ 2137 bool _is(E)() const @property nothrow @nogc 2138 if (is(E == T)) 2139 { 2140 return identifier__ == i; 2141 } 2142 2143 static if (is(T == void)) 2144 { 2145 /// Defined if `AllowedTypes` contains `void` 2146 static Algebraic _void() 2147 { 2148 Algebraic ret; 2149 ret.storage__ = () { 2150 import core.lifetime: forward; 2151 mixin(`Storage__ ret = { _member_` ~ i.stringof ~ ` : _Void!().init };`); 2152 return ret; 2153 } (); 2154 ret.identifier__ = i; 2155 return ret; 2156 } 2157 } 2158 else 2159 { 2160 /// 2161 static if (isCopyable!(const T) || is(Unqual!T == T)) 2162 this(T value) 2163 { 2164 import core.lifetime: forward; 2165 static if (is(T == typeof(null))) 2166 auto rhs = _Null!()(); 2167 else 2168 alias rhs = forward!value; 2169 2170 static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) 2171 { 2172 storage__.bytes = () @trusted { 2173 auto ret = _StorageI!i(rhs); 2174 return ret.bytes; 2175 } (); 2176 } 2177 else 2178 { 2179 storage__ = () { 2180 mixin(`Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs };`); 2181 return ret; 2182 } (); 2183 } 2184 static if (_Payload.length > 1) 2185 identifier__ = i; 2186 } 2187 2188 /// ditto 2189 static if (isCopyable!(const T)) 2190 this(const T value) const 2191 { 2192 static if (is(T == typeof(null))) 2193 auto rhs = _Null!()(); 2194 else 2195 alias rhs = value; 2196 static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) 2197 { 2198 storage__.bytes = () const @trusted { 2199 auto ret = const _StorageI!i(rhs); 2200 return ret.bytes; 2201 } (); 2202 } 2203 else 2204 { 2205 storage__ = () { 2206 mixin(`const Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs };`); 2207 return ret; 2208 } (); 2209 } 2210 static if (_Payload.length > 1) 2211 identifier__ = i; 2212 } 2213 2214 /// ditto 2215 static if (isCopyable!(immutable T)) 2216 this(immutable T value) immutable 2217 { 2218 static if (is(T == typeof(null))) 2219 auto rhs = _Null!()(); 2220 else 2221 alias rhs = value; 2222 static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) 2223 { 2224 storage__.bytes = () const @trusted { 2225 auto ret = immutable _StorageI!i(rhs); 2226 return ret.bytes; 2227 } (); 2228 } 2229 else 2230 { 2231 storage__ = () { 2232 mixin(`immutable Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs };`); 2233 return ret; 2234 } (); 2235 } 2236 static if (_Payload.length > 1) 2237 identifier__ = i; 2238 } 2239 2240 static if (__traits(compiles, (ref T a, ref T b) { moveEmplace(a, b); })) 2241 /// 2242 ref opAssign(T rhs) return @trusted 2243 { 2244 static foreach (T; MetaInfo__) 2245 __traits(getMember, this, T.tag) = T.Type.init; 2246 2247 import core.lifetime: forward; 2248 this = this.init; 2249 __ctor(forward!rhs); 2250 return this; 2251 } 2252 2253 /++ 2254 +/ 2255 bool opEquals()(scope ref const UnqualRec!T rhs) scope @trusted const //pure nothrow @nogc 2256 { 2257 static if (AllowedTypes.length > 1) 2258 if (identifier__ != i) 2259 return false; 2260 return trustedGet!T == rhs; 2261 } 2262 2263 ///ditto 2264 bool opEquals()(scope const UnqualRec!T rhs) scope @trusted const //pure nothrow @nogc 2265 { 2266 return opEquals(rhs); 2267 } 2268 2269 /++ 2270 +/ 2271 auto opCmp()(auto ref scope const UnqualRec!T rhs) scope @trusted const pure nothrow @nogc 2272 { 2273 static if (AllowedTypes.length > 1) 2274 if (auto d = int(identifier__) - int(i)) 2275 return d; 2276 static if (__traits(compiles, __cmp(trustedGet!T, rhs))) 2277 return __cmp(trustedGet!T, rhs); 2278 else 2279 static if (__traits(hasMember, T, "opCmp") && !is(T == U*, U)) 2280 return trustedGet!T.opCmp(rhs); 2281 else 2282 return trustedGet!T < rhs ? -1 : trustedGet!T > rhs ? +1 : 0; 2283 } 2284 2285 static if (is(Unqual!T == bool)) 2286 { 2287 private alias contains = Contains!AllowedTypes; 2288 static if (contains!long && !contains!int) 2289 { 2290 this(int value) 2291 { 2292 this(long(value)); 2293 } 2294 2295 this(int value) const 2296 { 2297 this(long(value)); 2298 } 2299 2300 this(int value) immutable 2301 { 2302 this(long(value)); 2303 } 2304 2305 ref opAssign(int rhs) return @trusted 2306 { 2307 return opAssign(long(rhs)); 2308 } 2309 2310 auto opEquals()(int rhs) const 2311 { 2312 return opEquals(long(rhs)); 2313 } 2314 2315 auto opCmp()(int rhs) const 2316 { 2317 return opCmp(long(rhs)); 2318 } 2319 } 2320 2321 static if (contains!ulong && !contains!uint) 2322 { 2323 this(uint value) 2324 { 2325 this(ulong(value)); 2326 } 2327 2328 this(uint value) const 2329 { 2330 this(ulong(value)); 2331 } 2332 2333 this(uint value) immutable 2334 { 2335 this(ulong(value)); 2336 } 2337 2338 ref opAssign(uint rhs) return @trusted 2339 { 2340 return opAssign(ulong(rhs)); 2341 } 2342 2343 auto opEquals()(uint rhs) const 2344 { 2345 return opEquals(ulong(rhs)); 2346 } 2347 2348 auto opCmp()(uint rhs) const 2349 { 2350 return opCmp(ulong(rhs)); 2351 } 2352 } 2353 } 2354 } 2355 } 2356 2357 static if (anySatisfy!(isErr, AllowedTypes)) 2358 { 2359 /++ 2360 Determines if the variant holds value of some none-$(LREF isVariant) type. 2361 The property is avaliable only for $(ResultVariant) 2362 +/ 2363 bool isOk() @safe pure nothrow @nogc const @property 2364 { 2365 switch (identifier__) 2366 { 2367 static foreach (i, T; AllowedTypes) 2368 { 2369 case i: 2370 return !.isErr!T; 2371 } 2372 default: assert(0); 2373 } 2374 } 2375 } 2376 } 2377 2378 /++ 2379 Constructor and methods propagation. 2380 +/ 2381 version(mir_core_test) 2382 unittest 2383 { 2384 static struct Base 2385 { 2386 double d; 2387 } 2388 2389 static class Cc 2390 { 2391 // alias this members are supported 2392 Base base; 2393 alias base this; 2394 2395 int a; 2396 private string _b; 2397 2398 @safe pure nothrow @nogc: 2399 2400 override size_t toHash() scope const { return hashOf(_b) ^ a; } 2401 2402 string b() const @property { return _b; } 2403 void b(string b) @property { _b = b; } 2404 2405 int retArg(int v) { return v; } 2406 string retArgT(TArgs...)(int v) { return TArgs.stringof; } 2407 2408 this(int a, string b) 2409 { 2410 this.a = a; 2411 this._b = b; 2412 } 2413 } 2414 2415 static struct S 2416 { 2417 string b; 2418 int a; 2419 2420 double retArg(double v) { return v; } 2421 double retArgT(TArgs...)(int v) { return v * TArgs.length; } 2422 2423 // alias this members are supported 2424 Base base; 2425 alias base this; 2426 } 2427 2428 static void inc(ref int a) { a++; } 2429 2430 alias V = Nullable!(Cc, S); // or Variant! 2431 2432 auto v = V(2, "str"); 2433 assert(v._is!Cc); 2434 assert(v.a == 2); 2435 assert(v.b == "str"); 2436 // members are returned by reference if possible 2437 inc(v.a); 2438 assert(v.a == 3); 2439 v.b = "s"; 2440 assert(v.b == "s"); 2441 // alias this members are supported 2442 v.d = 10; 2443 assert(v.d == 10); 2444 // method call support 2445 assert(v.retArg(100)._is!int); 2446 assert(v.retArg(100) == 100); 2447 2448 // method with template args support 2449 assert(v.retArgT!dchar(100)._is!string); 2450 assert(v.retArgT!dchar(100) == "(dchar)"); 2451 2452 v = V("S", 5); 2453 assert(v._is!S); 2454 assert(v.a == 5); 2455 assert(v.b == "S"); 2456 // members are returned by reference if possible 2457 inc(v.a); 2458 assert(v.a == 6); 2459 v.b = "s"; 2460 assert(v.b == "s"); 2461 // alias this members are supported 2462 v.d = 15; 2463 assert(v.d == 15); 2464 // method call support 2465 assert(v.retArg(300)._is!double); 2466 assert(v.retArg(300) == 300.0); 2467 } 2468 2469 // test CTFE 2470 unittest 2471 { 2472 static struct S { string s;} 2473 alias V = Nullable!(double, S); 2474 enum a = V(1.9); 2475 static assert (a == 1.9); 2476 enum b = V(S("str")); 2477 static assert(b == S("str")); 2478 static auto foo(int r) 2479 { 2480 auto s = V(S("str")); 2481 s = r; 2482 return s; 2483 } 2484 2485 static assert(foo(3) == 3); 2486 static auto bar(int r) 2487 { 2488 auto s = V(S("str")); 2489 s = r; 2490 return s.visit!((double d) => d, (_)=> 0.0)(); 2491 } 2492 assert(bar(3) == 3); 2493 static assert(bar(3) == 3); 2494 2495 static auto bar3(int r) 2496 { 2497 auto s = V(S("str")); 2498 s = r; 2499 return s.match!((double d) => d, (_)=> "3")(); 2500 } 2501 assert(bar(3) == 3); 2502 static assert(bar(3) == 3); 2503 } 2504 2505 @safe pure @nogc nothrow 2506 version(mir_core_test) unittest 2507 { 2508 import core.stdc.string: memcmp; 2509 2510 static struct C(ubyte payloadSize, bool isPOD, bool hasToHash = true, bool hasOpEquals = true) 2511 { 2512 ubyte[payloadSize] _payload; 2513 2514 const: 2515 2516 static if (!isPOD) 2517 { 2518 this(this) {} 2519 ~this() {} 2520 } 2521 2522 @safe pure nothrow @nogc: 2523 2524 2525 static if (hasToHash) 2526 size_t toHash() scope { return hashOf(_payload); } 2527 2528 static if (hasOpEquals) 2529 auto opEquals(ref const scope typeof(this) rhs) scope { return _payload == rhs._payload; } 2530 auto opCmp(ref const scope typeof(this) rhs) @trusted scope { return memcmp(_payload.ptr, rhs._payload.ptr, _payload.length); } 2531 } 2532 2533 static foreach (size1; [1, 2, 4, 8, 10, 16, 20]) 2534 static foreach (size2; [1, 2, 4, 8, 10, 16, 20]) 2535 static if (size1 != size2) 2536 static foreach (isPOD; [true, false]) 2537 static foreach (hasToHash; [true, false]) 2538 static foreach (hasOpEquals; [true, false]) 2539 {{ 2540 alias T = Variant!( 2541 double, 2542 C!(size1, isPOD, hasToHash, hasOpEquals), 2543 C!(size2, isPOD, hasToHash, hasOpEquals)); 2544 // static assert (__traits(compiles, T.init <= T.init)); 2545 }} 2546 } 2547 2548 // const propogation 2549 @safe pure nothrow @nogc 2550 version(mir_core_test) unittest 2551 { 2552 static struct S1 { immutable(ubyte)* value; } 2553 static struct C1 { immutable(uint)* value; } 2554 2555 alias V = Variant!(S1, C1); 2556 const V v = S1(); 2557 assert(v._is!S1); 2558 V w = v; 2559 w = v; 2560 2561 immutable f = V(S1()); 2562 auto t = immutable V(S1()); 2563 // auto j = immutable V(t); 2564 // auto i = const V(t); 2565 } 2566 2567 // ditto 2568 @safe pure nothrow @nogc 2569 version(mir_core_test) unittest 2570 { 2571 static struct S2 { 2572 uint* value; 2573 this(return ref scope const typeof(this) rhs) {} 2574 ref opAssign(typeof(this) rhs) return { return this; } 2575 } 2576 static struct C2 { const(uint)* value; } 2577 2578 alias V = Variant!(S2, C2); 2579 const V v = S2(); 2580 V w = v; 2581 w = S2(); 2582 w = v; 2583 w = cast(const) V.init; 2584 2585 const f = V(S2()); 2586 auto t = const V(f); 2587 } 2588 2589 @safe pure nothrow @nogc 2590 version(mir_core_test) unittest 2591 { 2592 static struct S3 { 2593 uint* value; 2594 this(return ref scope typeof(this) rhs) {} 2595 this(return ref scope const typeof(this) rhs) const {} 2596 this(return ref scope immutable typeof(this) rhs) immutable {} 2597 } 2598 static struct C3 { immutable(uint)* value; } 2599 2600 S3 s; 2601 S3 r = s; 2602 r = s; 2603 r = S3.init; 2604 2605 alias V = Variant!(S3, C3); 2606 V v = S3(); 2607 V w = v; 2608 w = S3(); 2609 w = V.init; 2610 w = v; 2611 2612 immutable V e = S3(); 2613 auto t = immutable V(S3()); 2614 auto j = const V(t); 2615 auto h = t; 2616 2617 immutable V l = C3(); 2618 auto g = immutable V(C3()); 2619 } 2620 2621 @safe pure nothrow @nogc 2622 version(mir_core_test) unittest 2623 { 2624 static struct S4 { 2625 uint* value; 2626 this(return ref scope const typeof(this) rhs) pure immutable {} 2627 } 2628 static struct C4 { immutable(uint)* value; } 2629 2630 2631 S4 s; 2632 S4 r = s; 2633 r = s; 2634 r = S4.init; 2635 2636 alias V = Variant!(S4, C4); 2637 V v = S4(); 2638 V w = v; 2639 w = S4(); 2640 w = V.init; 2641 w = v; 2642 2643 { 2644 const V e = S4(); 2645 const k = w; 2646 auto t = const V(k); 2647 auto j = immutable V(k); 2648 } 2649 2650 immutable V e = S4(); 2651 immutable k = w; 2652 auto t = immutable V(S4()); 2653 auto j = const V(t); 2654 auto h = t; 2655 2656 immutable V l = C4(); 2657 import core.lifetime; 2658 auto g = immutable V(C4()); 2659 immutable b = immutable V(s); 2660 } 2661 2662 @safe pure nothrow @nogc 2663 version(mir_core_test) unittest 2664 { 2665 import core.lifetime: move; 2666 2667 static struct S5 { 2668 immutable(uint)* value; 2669 this(return ref scope typeof(this) rhs) {} 2670 this(return ref scope const typeof(this) rhs) immutable {} 2671 } 2672 static struct C5 { immutable(uint)* value; } 2673 2674 S5 s; 2675 S5 r = s; 2676 r = s; 2677 r = S5.init; 2678 2679 alias V = Variant!(S5, C5); 2680 V v = S5(); 2681 V w = v; 2682 w = S5(); 2683 w = V.init; 2684 w = v; 2685 2686 immutable V e = S5(); 2687 immutable f = V(S5()); 2688 immutable k = w; 2689 auto t = immutable V(S5()); 2690 auto j = const V(t); 2691 auto h = t; 2692 2693 immutable V l = C5(); 2694 import core.lifetime; 2695 immutable n = w.move; 2696 auto g = immutable V(C5()); 2697 immutable b = immutable V(s); 2698 } 2699 2700 @safe pure nothrow @nogc 2701 version(mir_core_test) unittest 2702 { 2703 static struct S { 2704 uint* value; 2705 this(this) @safe pure nothrow @nogc {} 2706 // void opAssign(typeof(this) rhs) {} 2707 } 2708 static struct C1 { const(uint)* value; } 2709 2710 S s; 2711 S r = s; 2712 r = s; 2713 r = S.init; 2714 2715 alias V = Variant!(S, C1); 2716 V v = S(); 2717 V w = v; 2718 w = S(); 2719 w = V.init; 2720 w = v; 2721 } 2722 2723 /++ 2724 Applies a delegate or function to the given Variant depending on the held type, 2725 ensuring that all types are handled by the visiting functions. 2726 +/ 2727 alias visit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, false); 2728 2729 /// 2730 @safe pure @nogc nothrow 2731 version(mir_core_test) unittest 2732 { 2733 alias Number = Variant!(int, double); 2734 2735 Number x = 23; 2736 Number y = 1.0; 2737 2738 assert(x.visit!((int v) => true, (float v) => false)); 2739 assert(y.visit!((int v) => false, (float v) => true)); 2740 } 2741 2742 /// 2743 @safe pure @nogc 2744 version(mir_core_test) unittest 2745 { 2746 alias Number = Nullable!(int, double); 2747 2748 Number z = null; // default 2749 Number x = 23; 2750 Number y = 1.0; 2751 2752 () nothrow { 2753 assert(x.visit!((int v) => true, (float v) => false)); 2754 assert(y.visit!((int v) => false, (v) => true)); 2755 assert(z.visit!((typeof(null) v) => true, (v) => false)); 2756 } (); 2757 2758 auto xx = x.get; 2759 static assert (is(typeof(xx) == Variant!(int, double))); 2760 assert(xx.visit!((int v) => v, (float v) => 0) == 23); 2761 assert(xx.visit!((ref v) => v) == 23); 2762 2763 x = null; 2764 y.nullify; 2765 2766 assert(x.isNull); 2767 assert(y.isNull); 2768 assert(z.isNull); 2769 assert(z == y); 2770 } 2771 2772 /// Array primitives propagation 2773 @safe pure version(mir_core_test) unittest 2774 { 2775 Variant!(long[], double[]) array; 2776 array = new long[3]; 2777 array[2] = 100; 2778 assert(array == [0L, 0, 100]); 2779 assert(array.length == 3); 2780 assert(array[2] == 100); 2781 array.length = 4; 2782 assert(array == [0L, 0, 100, 0]); 2783 array = array[2 .. 3]; 2784 assert(array.length == 1); 2785 assert(array[0] == 100); 2786 array[0] = 10.Variant!(long, double); 2787 assert(array[0] == 10); 2788 } 2789 2790 /++ 2791 Checks $(LREF .Algebraic.toString) and `void` 2792 $(LREF Algerbraic)`.toString` requries `mir-algorithm` package 2793 +/ 2794 @safe pure nothrow version(mir_core_test) unittest 2795 { 2796 import mir.conv: to; 2797 enum MIR_ALGORITHM = __traits(compiles, { import mir.format; }); 2798 2799 alias visitorHandler = visit!( 2800 (typeof(null)) => "NULL", 2801 () => "VOID", 2802 (ref r) {r += 1;}, // returns void 2803 ); 2804 2805 alias secondOrderVisitorHandler = visit!( 2806 () => "SO VOID", // void => to "RV VOID" 2807 (str) => str, // string to => it self 2808 ); 2809 2810 alias V = Nullable!(void, int); 2811 static assert(is(V == Variant!(typeof(null), void, int))); 2812 2813 V variant; 2814 2815 assert(secondOrderVisitorHandler(visitorHandler(variant)) == "NULL"); 2816 assert(variant.toString == "null"); 2817 2818 variant = V._void; 2819 assert(variant._is!void); 2820 assert(is(typeof(variant.get!void()) == void)); 2821 2822 assert(secondOrderVisitorHandler(visitorHandler(variant)) == "VOID"); 2823 assert(variant.toString == "void"); 2824 2825 variant = 5; 2826 2827 assert(secondOrderVisitorHandler(visitorHandler(variant)) == "SO VOID"); 2828 assert(variant == 6); 2829 assert(variant.toString == (MIR_ALGORITHM ? "6" : "int")); 2830 } 2831 2832 version(mir_core_test) 2833 unittest 2834 { 2835 Nullable!() value; 2836 alias visitHandler = visit!((typeof(null)) => null, err); 2837 auto d = visitHandler(value); 2838 assert(d == value); 2839 } 2840 2841 /++ 2842 Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions. 2843 Throws: Exception if `naryFun!visitors` can't be called with provided arguments 2844 +/ 2845 alias tryVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.exception, false); 2846 2847 /// 2848 @safe pure @nogc 2849 version(mir_core_test) unittest 2850 { 2851 alias Number = Variant!(int, double); 2852 2853 Number x = 23; 2854 2855 assert(x.tryVisit!((int v) => true)); 2856 } 2857 2858 /++ 2859 Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions. 2860 Returns: nullable variant, null value is used if `naryFun!visitors` can't be called with provided arguments. 2861 +/ 2862 alias optionalVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.nullable, false); 2863 2864 /// 2865 @safe pure @nogc nothrow 2866 version(mir_core_test) unittest 2867 { 2868 static struct S { int a; } 2869 2870 Variant!(S, double) variant; 2871 2872 alias optionalVisitInst = optionalVisit!((ref value) => value + 0); 2873 2874 // do nothing because of variant isn't initialized 2875 Nullable!double result = optionalVisitInst(variant); 2876 assert(result.isNull); 2877 2878 variant = S(2); 2879 // do nothing because of lambda can't compile 2880 result = optionalVisitInst(variant); 2881 assert(result == null); 2882 2883 variant = 3.0; 2884 result = optionalVisitInst(variant); 2885 assert (result == 3.0); 2886 } 2887 2888 /++ 2889 Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions. 2890 Returns: optionally nullable type, null value is used if `naryFun!visitors` can't be called with provided arguments. 2891 +/ 2892 alias autoVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.auto_, false); 2893 2894 2895 /++ 2896 Applies a delegate or function to the given arguments depending on the held type, 2897 ensuring that all types are handled by the visiting functions. 2898 2899 The handler supports multiple dispatch or multimethods: a feature of handler in which 2900 a function or method can be dynamically dispatched based on the run time (dynamic) type or, 2901 in the more general case, some other attribute of more than one of its arguments. 2902 2903 Fuses algebraic types on return. 2904 2905 See_also: $(HTTPS en.wikipedia.org/wiki/Multiple_dispatch, Multiple dispatch) 2906 +/ 2907 alias match(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, true); 2908 2909 /// 2910 version(mir_core_test) 2911 unittest 2912 { 2913 static struct Asteroid { uint size; } 2914 static struct Spaceship { uint size; } 2915 alias SpaceObject = Variant!(Asteroid, Spaceship); 2916 2917 alias collideWith = match!( 2918 (Asteroid x, Asteroid y) => "a/a", 2919 (Asteroid x, Spaceship y) => "a/s", 2920 (Spaceship x, Asteroid y) => "s/a", 2921 (Spaceship x, Spaceship y) => "s/s", 2922 ); 2923 2924 import mir.utility: min; 2925 2926 // Direct access of a member in case of all algebraic types has this member 2927 alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1; 2928 2929 alias collide = (x, y) => oops(x, y) ? "big-boom" : collideWith(x, y); 2930 2931 auto ea = Asteroid(1); 2932 auto es = Spaceship(2); 2933 auto oa = SpaceObject(ea); 2934 auto os = SpaceObject(es); 2935 2936 // Asteroid-Asteroid 2937 assert(collide(ea, ea) == "a/a"); 2938 assert(collide(ea, oa) == "a/a"); 2939 assert(collide(oa, ea) == "a/a"); 2940 assert(collide(oa, oa) == "a/a"); 2941 2942 // Asteroid-Spaceship 2943 assert(collide(ea, es) == "a/s"); 2944 assert(collide(ea, os) == "a/s"); 2945 assert(collide(oa, es) == "a/s"); 2946 assert(collide(oa, os) == "a/s"); 2947 2948 // Spaceship-Asteroid 2949 assert(collide(es, ea) == "s/a"); 2950 assert(collide(es, oa) == "s/a"); 2951 assert(collide(os, ea) == "s/a"); 2952 assert(collide(os, oa) == "s/a"); 2953 2954 // Spaceship-Spaceship 2955 assert(collide(es, es) == "big-boom"); 2956 assert(collide(es, os) == "big-boom"); 2957 assert(collide(os, es) == "big-boom"); 2958 assert(collide(os, os) == "big-boom"); 2959 } 2960 2961 /++ 2962 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions. 2963 Throws: Exception if `naryFun!visitors` can't be called with provided arguments 2964 2965 Fuses algebraic types on return. 2966 +/ 2967 alias tryMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.exception, true); 2968 2969 /// 2970 version(mir_core_test) 2971 unittest 2972 { 2973 import std.exception: assertThrown; 2974 static struct Asteroid { uint size; } 2975 static struct Spaceship { uint size; } 2976 alias SpaceObject = Variant!(Asteroid, Spaceship); 2977 2978 alias collideWith = tryMatch!( 2979 (Asteroid x, Asteroid y) => "a/a", 2980 // No visitor for A/S pair 2981 // (Asteroid x, Spaceship y) => "a/s", 2982 (Spaceship x, Asteroid y) => "s/a", 2983 (Spaceship x, Spaceship y) => "s/s", 2984 ); 2985 2986 import mir.utility: min; 2987 // Direct access of a member in case of all algebraic types has this member 2988 alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1; 2989 2990 alias collide = (x, y) => oops(x, y) ? "big-boom" : collideWith(x, y); 2991 2992 auto ea = Asteroid(1); 2993 auto es = Spaceship(2); 2994 auto oa = SpaceObject(ea); 2995 auto os = SpaceObject(es); 2996 2997 // Asteroid-Asteroid 2998 assert(collide(ea, ea) == "a/a"); 2999 assert(collide(ea, oa) == "a/a"); 3000 assert(collide(oa, ea) == "a/a"); 3001 assert(collide(oa, oa) == "a/a"); 3002 3003 // Asteroid-Spaceship 3004 assertThrown!Exception(collide(ea, es)); 3005 assertThrown!Exception(collide(ea, os)); 3006 assertThrown!Exception(collide(oa, es)); 3007 assertThrown!Exception(collide(oa, os)); 3008 3009 // can deduce the type based on other return values 3010 static assert(is(typeof(collide(ea, os)) == string)); 3011 static assert(is(typeof(collide(oa, es)) == string)); 3012 static assert(is(typeof(collide(oa, os)) == string)); 3013 3014 // Also allows newer compilers to detect combinations which always throw an exception 3015 static if (is(typeof(collideWith(ea, es)) == noreturn)) 3016 { 3017 static assert(is(typeof(collide(ea, es)) == string)); 3018 } 3019 else 3020 { 3021 // not enough information to deduce the type from (ea, es) pair 3022 static assert(is(typeof(collide(ea, es)) == void)); 3023 } 3024 3025 // Spaceship-Asteroid 3026 assert(collide(es, ea) == "s/a"); 3027 assert(collide(es, oa) == "s/a"); 3028 assert(collide(os, ea) == "s/a"); 3029 assert(collide(os, oa) == "s/a"); 3030 3031 // Spaceship-Spaceship 3032 assert(collide(es, es) == "big-boom"); 3033 assert(collide(es, os) == "big-boom"); 3034 assert(collide(os, es) == "big-boom"); 3035 assert(collide(os, os) == "big-boom"); 3036 } 3037 3038 /++ 3039 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions. 3040 Returns: nullable variant, null value is used if `naryFun!visitors` can't be called with provided arguments. 3041 3042 Fuses algebraic types on return. 3043 +/ 3044 alias optionalMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.nullable, true); 3045 3046 /// 3047 version(mir_core_test) 3048 unittest 3049 { 3050 static struct Asteroid { uint size; } 3051 static struct Spaceship { uint size; } 3052 alias SpaceObject = Variant!(Asteroid, Spaceship); 3053 3054 alias collideWith = optionalMatch!( 3055 (Asteroid x, Asteroid y) => "a/a", 3056 // No visitor for A/S pair 3057 // (Asteroid x, Spaceship y) => "a/s", 3058 (Spaceship x, Asteroid y) => "s/a", 3059 (Spaceship x, Spaceship y) => "s/s", 3060 ); 3061 3062 import mir.utility: min; 3063 // Direct access of a member in case of all algebraic types has this member 3064 alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1; 3065 3066 alias collide = (x, y) => oops(x, y) ? "big-boom".nullable : collideWith(x, y); 3067 3068 auto ea = Asteroid(1); 3069 auto es = Spaceship(2); 3070 auto oa = SpaceObject(ea); 3071 auto os = SpaceObject(es); 3072 3073 // Asteroid-Asteroid 3074 assert(collide(ea, ea) == "a/a"); 3075 assert(collide(ea, oa) == "a/a"); 3076 assert(collide(oa, ea) == "a/a"); 3077 assert(collide(oa, oa) == "a/a"); 3078 3079 // Asteroid-Spaceship 3080 // assert(collide(ea, es).isNull); // Compiler error: incompatible types 3081 assert(collideWith(ea, es).isNull); // OK 3082 assert(collide(ea, os).isNull); 3083 assert(collide(oa, es).isNull); 3084 assert(collide(oa, os).isNull); 3085 3086 3087 // Spaceship-Asteroid 3088 assert(collide(es, ea) == "s/a"); 3089 assert(collide(es, oa) == "s/a"); 3090 assert(collide(os, ea) == "s/a"); 3091 assert(collide(os, oa) == "s/a"); 3092 3093 // Spaceship-Spaceship 3094 assert(collide(es, es) == "big-boom"); 3095 assert(collide(es, os) == "big-boom"); 3096 assert(collide(os, es) == "big-boom"); 3097 assert(collide(os, os) == "big-boom"); 3098 3099 // check types 3100 3101 static assert(!__traits(compiles, collide(Asteroid.init, Spaceship.init))); 3102 static assert(is(typeof(collideWith(Asteroid.init, Spaceship.init)) == Nullable!())); 3103 3104 static assert(is(typeof(collide(Asteroid.init, Asteroid.init)) == Nullable!string)); 3105 static assert(is(typeof(collide(Asteroid.init, SpaceObject.init)) == Nullable!string)); 3106 static assert(is(typeof(collide(SpaceObject.init, Asteroid.init)) == Nullable!string)); 3107 static assert(is(typeof(collide(SpaceObject.init, SpaceObject.init)) == Nullable!string)); 3108 static assert(is(typeof(collide(SpaceObject.init, Spaceship.init)) == Nullable!string)); 3109 static assert(is(typeof(collide(Spaceship.init, Asteroid.init)) == Nullable!string)); 3110 static assert(is(typeof(collide(Spaceship.init, SpaceObject.init)) == Nullable!string)); 3111 static assert(is(typeof(collide(Spaceship.init, Spaceship.init)) == Nullable!string)); 3112 } 3113 3114 /++ 3115 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions. 3116 Returns: optionally nullable type, null value is used if `naryFun!visitors` can't be called with provided arguments. 3117 3118 Fuses algebraic types on return. 3119 +/ 3120 alias autoMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.auto_, true); 3121 3122 /// 3123 version(mir_core_test) 3124 unittest 3125 { 3126 static struct Asteroid { uint size; } 3127 static struct Spaceship { uint size; } 3128 alias SpaceObject = Variant!(Asteroid, Spaceship); 3129 3130 alias collideWith = autoMatch!( 3131 (Asteroid x, Asteroid y) => "a/a", 3132 // No visitor for A/S pair 3133 // (Asteroid x, Spaceship y) => "a/s", 3134 (Spaceship x, Asteroid y) => "s/a", 3135 (Spaceship x, Spaceship y) => "s/s", 3136 ); 3137 3138 import mir.utility: min; 3139 // Direct access of a member in case of all algebraic types has this member 3140 alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1; 3141 3142 import mir.conv: to; 3143 alias collide = (x, y) => oops(x, y) ? "big-boom".to!(typeof(collideWith(x, y))) : collideWith(x, y); 3144 3145 auto ea = Asteroid(1); 3146 auto es = Spaceship(2); 3147 auto oa = SpaceObject(ea); 3148 auto os = SpaceObject(es); 3149 3150 // Asteroid-Asteroid 3151 assert(collide(ea, ea) == "a/a"); 3152 assert(collide(ea, oa) == "a/a"); 3153 assert(collide(oa, ea) == "a/a"); 3154 assert(collide(oa, oa) == "a/a"); 3155 3156 // Asteroid-Spaceship 3157 // assert(collide(ea, es).isNull); // Compiler error: incompatible types 3158 assert(collideWith(ea, es).isNull); // OK 3159 assert(collide(ea, os).isNull); 3160 assert(collide(oa, es).isNull); 3161 assert(collide(oa, os).isNull); 3162 3163 // Spaceship-Asteroid 3164 assert(collide(es, ea) == "s/a"); 3165 assert(collide(es, oa) == "s/a"); 3166 assert(collide(os, ea) == "s/a"); 3167 assert(collide(os, oa) == "s/a"); 3168 3169 // Spaceship-Spaceship 3170 assert(collide(es, es) == "big-boom"); 3171 assert(collide(es, os) == "big-boom"); 3172 assert(collide(os, es) == "big-boom"); 3173 assert(collide(os, os) == "big-boom"); 3174 3175 // check types 3176 3177 static assert(!__traits(compiles, collide(Asteroid.init, Spaceship.init))); 3178 static assert(is(typeof(collideWith(Asteroid.init, Spaceship.init)) == Nullable!())); 3179 3180 static assert(is(typeof(collide(Asteroid.init, Asteroid.init)) == string)); 3181 static assert(is(typeof(collide(SpaceObject.init, Asteroid.init)) == string)); 3182 static assert(is(typeof(collide(Spaceship.init, Asteroid.init)) == string)); 3183 static assert(is(typeof(collide(Spaceship.init, SpaceObject.init)) == string)); 3184 static assert(is(typeof(collide(Spaceship.init, Spaceship.init)) == string)); 3185 3186 static assert(is(typeof(collide(Asteroid.init, SpaceObject.init)) == Nullable!string)); 3187 static assert(is(typeof(collide(SpaceObject.init, SpaceObject.init)) == Nullable!string)); 3188 static assert(is(typeof(collide(SpaceObject.init, Spaceship.init)) == Nullable!string)); 3189 } 3190 3191 /++ 3192 Applies a member handler to the given Variant depending on the held type, 3193 ensuring that all types are handled by the visiting handler. 3194 +/ 3195 alias getMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.compileTime, false); 3196 3197 /// 3198 @safe pure @nogc nothrow 3199 version(mir_core_test) unittest 3200 { 3201 static struct S { auto bar(int a) { return a; } enum boolean = true; } 3202 static struct C2 { alias bar = (double a) => a * 2; enum boolean = false; } 3203 3204 alias V = Variant!(S, C2); 3205 3206 V x = S(); 3207 V y = C2(); 3208 3209 static assert(is(typeof(x.getMember!"bar"(2)) == Variant!(int, double))); 3210 assert(x.getMember!"bar"(2) == 2); 3211 assert(y.getMember!"bar"(2) != 4); 3212 assert(y.getMember!"bar"(2) == 4.0); 3213 3214 // direct implementation 3215 assert(x.bar(2) == 2); 3216 assert(y.bar(2) != 4); 3217 assert(y.bar(2) == 4.0); 3218 assert(x.boolean); 3219 assert(!y.boolean); 3220 } 3221 3222 /++ 3223 Applies a member handler to the given Variant depending on the held type, 3224 ensuring that all types are handled by the visiting handler. 3225 3226 Fuses algebraic types on return. 3227 +/ 3228 alias matchMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.compileTime, true); 3229 3230 /// 3231 @safe pure @nogc nothrow 3232 version(mir_core_test) unittest 3233 { 3234 static struct S 3235 { 3236 Nullable!int m; 3237 } 3238 3239 static struct C1 3240 { 3241 Variant!(float, double) m; 3242 } 3243 3244 alias V = Variant!(S, C1); 3245 3246 V x = S(2.nullable); 3247 V y = C1(Variant!(float, double)(4.0)); 3248 3249 // getMember returns an algebraic of algebraics 3250 static assert(is(typeof(x.getMember!"m") == Variant!(Variant!(float, double), Nullable!int))); 3251 // matchMember returns a fused algebraic 3252 static assert(is(typeof(x.matchMember!"m") == Nullable!(int, float, double))); 3253 assert(x.matchMember!"m" == 2); 3254 assert(y.matchMember!"m" != 4); 3255 assert(y.matchMember!"m" == 4.0); 3256 } 3257 3258 /++ 3259 Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor. 3260 Throws: Exception if member can't be accessed with provided arguments 3261 +/ 3262 alias tryGetMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.exception, false); 3263 3264 /// 3265 @safe pure @nogc 3266 version(mir_core_test) unittest 3267 { 3268 static struct S { int bar(int a) { return a; }} 3269 static struct C3 { alias Bar = (double a) => a * 2; } 3270 3271 alias V = Variant!(S, C3); 3272 3273 V x = S(); 3274 V y = C3(); 3275 3276 static assert(is(typeof(x.tryGetMember!"bar"(2)) == int)); 3277 static assert(is(typeof(y.tryGetMember!"Bar"(2)) == double)); 3278 assert(x.tryGetMember!"bar"(2) == 2); 3279 assert(y.tryGetMember!"Bar"(2) == 4.0); 3280 } 3281 3282 /// 3283 @safe pure @nogc nothrow 3284 version(mir_core_test) unittest 3285 { 3286 alias Number = Variant!(int, double); 3287 3288 Number x = Number(23); 3289 Number y = Number(1.0); 3290 3291 assert(x.visit!((int v) => true, (float v) => false)); 3292 assert(y.visit!((int v) => false, (float v) => true)); 3293 } 3294 3295 /++ 3296 Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor. 3297 Throws: Exception if member can't be accessed with provided arguments 3298 3299 Fuses algebraic types on return. 3300 +/ 3301 alias tryMatchMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.exception, true); 3302 3303 /++ 3304 Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor. 3305 Returns: nullable variant, null value is used if the member can't be called with provided arguments. 3306 +/ 3307 alias optionalGetMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.nullable, false); 3308 3309 /++ 3310 Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor. 3311 Returns: nullable variant, null value is used if the member can't be called with provided arguments. 3312 3313 Fuses algebraic types on return. 3314 +/ 3315 alias optionalMatchMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.nullable, true); 3316 3317 /++ 3318 Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor. 3319 Returns: optionally nullable type, null value is used if the member can't be called with provided arguments. 3320 +/ 3321 alias autoGetMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.auto_, false); 3322 3323 /++ 3324 Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor. 3325 Returns: optionally nullable type, null value is used if the member can't be called with provided arguments. 3326 3327 Fuses algebraic types on return. 3328 +/ 3329 alias autoMatchMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.auto_, true); 3330 3331 private template getMemberHandler(string member, TArgs...) 3332 { 3333 /// 3334 auto ref getMemberHandler(V, Args...)(ref V value, auto ref Args args) 3335 { 3336 static if (Args.length == 0) 3337 { 3338 static if (TArgs.length) 3339 { 3340 return mixin(`value.` ~ member ~ `!TArgs`); 3341 } 3342 else 3343 { 3344 return __traits(getMember, value, member); 3345 } 3346 } 3347 else 3348 { 3349 import core.lifetime: forward; 3350 import mir.reflection: hasField; 3351 static if (TArgs.length) 3352 { 3353 static if (hasField!(V, member) && Args.length == 1) 3354 return mixin(`value.` ~ member ~ `!TArgs`) = forward!args; 3355 else 3356 return mixin(`value.` ~ member ~ `!TArgs(forward!args)`); 3357 } 3358 else 3359 { 3360 static if (hasField!(V, member) && Args.length == 1) 3361 return __traits(getMember, value, member) = forward!args; 3362 else 3363 return __traits(getMember, value, member)(forward!args); 3364 } 3365 } 3366 } 3367 } 3368 3369 private template VariantReturnTypes(T...) 3370 { 3371 import std.meta: staticMap; 3372 3373 alias VariantReturnTypes = NoDuplicates!(staticMap!(TryRemoveConst, T)); 3374 } 3375 3376 private enum Exhaustive 3377 { 3378 compileTime, 3379 exception, 3380 nullable, 3381 auto_, 3382 } 3383 3384 private template nextVisitor(T, alias visitor, alias arg) 3385 { 3386 static if (is(T == void)) 3387 { 3388 alias nextVisitor = visitor; 3389 } 3390 else 3391 auto ref nextVisitor(NextArgs...)(auto ref NextArgs nextArgs) 3392 { 3393 import core.lifetime: forward; 3394 3395 static if (__traits(isRef, arg)) 3396 return visitor(arg.trustedGet!T, forward!nextArgs); 3397 else 3398 static if (is(typeof(move(arg.trustedGet!T)))) 3399 return visitor(move(arg.trustedGet!T), forward!nextArgs); 3400 else 3401 return visitor((() => arg.trustedGet!T)(), forward!nextArgs); 3402 } 3403 } 3404 3405 private template nextVisitor(alias visitor, alias arg) 3406 { 3407 auto ref nextVisitor(NextArgs...)(auto ref NextArgs nextArgs) 3408 { 3409 import core.lifetime: forward; 3410 return visitor(forward!arg, forward!nextArgs); 3411 } 3412 } 3413 3414 private template visitThis(alias visitor, Exhaustive nextExhaustive) 3415 { 3416 template visitThis(T) 3417 { 3418 auto ref visitThis(Args...)(auto ref Args args) 3419 { 3420 import core.lifetime: forward; 3421 return .visitImpl!(nextVisitor!(T, visitor, forward!(args[0])), nextExhaustive, true)(forward!(args[1 .. $])); 3422 } 3423 } 3424 } 3425 3426 private template visitLast(alias visitor) 3427 { 3428 template visitLast(T) 3429 { 3430 static if (is(T == void)) 3431 { 3432 auto ref visitLast(Args...)(auto ref Args args) 3433 { 3434 import core.lifetime: forward; 3435 return visitor(forward!(args[1 .. $])); 3436 } 3437 } 3438 else 3439 { 3440 auto ref visitLast(Args...)(auto ref Args args) 3441 { 3442 import core.lifetime: forward, move; 3443 static if (__traits(isRef, args[0])) 3444 return visitor(args[0].trustedGet!T, forward!(args[1 .. $])); 3445 else 3446 static if (is(typeof(move(args[0].trustedGet!T)))) 3447 return visitor(move(args[0].trustedGet!T), forward!(args[1 .. $])); 3448 else 3449 return visitor((() => args[0].trustedGet!T)(), forward!(args[1 .. $])); 3450 } 3451 } 3452 } 3453 } 3454 3455 private enum _AcceptAll(Args...) = true; 3456 3457 template visitImpl(alias visitor, Exhaustive exhaustive, bool fused, alias Filter = _AcceptAll) 3458 { 3459 /// 3460 auto ref visitImpl(Args...)(auto ref Args args) 3461 if (Filter!Args) 3462 { 3463 import std.meta: anySatisfy, staticMap, AliasSeq; 3464 import core.lifetime: forward; 3465 3466 static if (!anySatisfy!(isLikeVariant, Args)) 3467 { 3468 static if (exhaustive == Exhaustive.compileTime) 3469 { 3470 return visitor(forward!args); 3471 } 3472 else 3473 static if (exhaustive == Exhaustive.exception) 3474 { 3475 static if (__traits(compiles, visitor(forward!args))) 3476 return visitor(forward!args); 3477 else 3478 return throwMe(variantMemberException); 3479 } 3480 else 3481 static if (exhaustive == Exhaustive.nullable) 3482 { 3483 static if (__traits(compiles, visitor(forward!args))) 3484 return Nullable!(typeof(visitor(forward!args)))(visitor(forward!args)); 3485 else 3486 return Nullable!().init; 3487 } 3488 else 3489 static if (exhaustive == Exhaustive.auto_) 3490 { 3491 static if (__traits(compiles, visitor(forward!args))) 3492 return visitor(forward!args); 3493 else 3494 return Nullable!().init; 3495 } 3496 else 3497 static assert(0, "not implemented"); 3498 } 3499 else 3500 static if (!isLikeVariant!(Args[0])) 3501 { 3502 return .visitImpl!(nextVisitor!(visitor, args[0]), exhaustive, fused)(forward!(args[1 .. $])); 3503 } 3504 else 3505 { 3506 static if (fused && anySatisfy!(isLikeVariant, Args[1 .. $])) 3507 { 3508 alias fun = visitThis!(visitor, exhaustive); 3509 } 3510 else 3511 { 3512 static assert (isLikeVariant!(Args[0]), "First argument should be a Mir Algebraic type"); 3513 alias fun = visitLast!visitor; 3514 } 3515 3516 template VariantReturnTypesImpl(T) 3517 { 3518 static if (__traits(compiles, fun!T(forward!args))) 3519 { 3520 alias R = typeof(fun!T(forward!args)); 3521 static if (fused && isLikeVariant!R) 3522 alias VariantReturnTypesImpl = staticMap!(TryRemoveConst, R.AllowedTypes); 3523 else 3524 static if (is(immutable R == immutable noreturn)) 3525 alias VariantReturnTypesImpl = AliasSeq!(); 3526 else 3527 alias VariantReturnTypesImpl = AliasSeq!(TryRemoveConst!R); 3528 } 3529 else 3530 static if (exhaustive == Exhaustive.auto_) 3531 alias VariantReturnTypesImpl = AliasSeq!(typeof(null)); 3532 else 3533 alias VariantReturnTypesImpl = AliasSeq!(); 3534 } 3535 3536 static if (exhaustive == Exhaustive.nullable) 3537 alias AllReturnTypes = NoDuplicates!(typeof(null), staticMap!(VariantReturnTypesImpl, Args[0].AllowedTypes)); 3538 else 3539 alias AllReturnTypes = NoDuplicates!(staticMap!(VariantReturnTypesImpl, Args[0].AllowedTypes)); 3540 3541 switch (args[0].identifier__) 3542 { 3543 static foreach (i, T; Args[0].AllowedTypes) 3544 { 3545 case i: 3546 static if (__traits(compiles, fun!T(forward!args)) || exhaustive == Exhaustive.compileTime && !is(T == typeof(null))) 3547 { 3548 static if (AllReturnTypes.length == 1) 3549 { 3550 return fun!T(forward!args); 3551 } 3552 else 3553 static if (is(VariantReturnTypesImpl!T == AliasSeq!void)) 3554 { 3555 fun!T(forward!args); 3556 return Variant!AllReturnTypes._void; 3557 } 3558 else 3559 static if (is(typeof(fun!T(forward!args)) == Variant!AllReturnTypes)) 3560 { 3561 return fun!T(forward!args); 3562 } 3563 else 3564 { 3565 return Variant!AllReturnTypes(fun!T(forward!args)); 3566 } 3567 } 3568 else 3569 static if (exhaustive == Exhaustive.compileTime && is(T == typeof(null))) 3570 { 3571 assert(0, "Null " ~ Args[0].stringof); 3572 } 3573 else 3574 static if (exhaustive == Exhaustive.nullable || exhaustive == Exhaustive.auto_) 3575 { 3576 return Variant!AllReturnTypes(null); 3577 } 3578 else 3579 { 3580 return throwMe(variantMemberException); 3581 } 3582 } 3583 default: assert(0); 3584 } 3585 } 3586 } 3587 } 3588 3589 private string enumKindText()(scope const char[][] strs) 3590 { 3591 auto r = "enum Kind {"; 3592 foreach (s; strs) 3593 { 3594 r ~= s; 3595 r ~= ", "; 3596 } 3597 r ~= "}"; 3598 return r; 3599 } 3600 3601 @safe pure @nogc 3602 version(mir_core_test) unittest 3603 { 3604 static struct S { int a; } 3605 3606 Variant!(S, double) variant; 3607 variant = 1.0; 3608 variant.tryVisit!((ref value, b) => value += b)(2); 3609 assert (variant.get!double == 3); 3610 3611 alias fun = (ref value) { 3612 static if (is(typeof(value) == S)) 3613 value.a += 2; 3614 else 3615 value += 2; 3616 }; 3617 3618 variant.tryVisit!fun; 3619 assert (variant.get!double == 5); 3620 3621 variant = S(4); 3622 variant.tryVisit!fun; 3623 assert (variant.get!S.a == 6); 3624 3625 alias D = Variant!(Variant!(S, double)); 3626 } 3627 3628 @safe pure @nogc 3629 version(mir_core_test) unittest 3630 { 3631 import std.meta: AliasSeq; 3632 3633 static struct PODWithLongPointer { 3634 long* x; 3635 this(long l) pure 3636 { 3637 x = new long(l); 3638 } 3639 3640 @property: 3641 long a() const { 3642 return x ? *x : 0; 3643 } 3644 3645 void a(long l) { 3646 if (x) { 3647 *x = l; 3648 } else { 3649 x = new long(l); 3650 } 3651 } 3652 } 3653 static assert(is(TypeSet!(byte, immutable PODWithLongPointer) == AliasSeq!(byte, immutable PODWithLongPointer))); 3654 } 3655 3656 private enum isSimpleAggregateType(T) = is(T == class) || is(T == struct) || is(T == union) || is(T == interface); 3657 3658 unittest 3659 { 3660 static struct Test 3661 { 3662 alias Value = void; 3663 } 3664 3665 alias B = Nullable!Test; 3666 } 3667 3668 /++ 3669 Wrapper to denote an error value type. 3670 3671 The wrapper is autostripped by $(LREF none). 3672 3673 See_also: $(LREF reflectErr). 3674 +/ 3675 template Err(T) 3676 { 3677 static if (!isErr!T) 3678 { 3679 /// 3680 struct Err 3681 { 3682 /// 3683 T value; 3684 } 3685 } 3686 else 3687 { 3688 alias Err = T; 3689 } 3690 } 3691 3692 /// ditto 3693 auto err(T)(T value) { 3694 import core.lifetime: move; 3695 static if (isErr!T) 3696 return move(value); 3697 else 3698 return Err!T(move(value)); 3699 } 3700 3701 /// 3702 unittest 3703 { 3704 @reflectErr static struct E {} 3705 3706 static assert(is(Err!string == Err!string)); 3707 static assert(is(Err!(Err!string) == Err!string)); 3708 static assert(is(Err!E == E)); 3709 static assert(is(Err!Exception == Exception)); 3710 3711 static assert(is(typeof("str".err) == Err!string)); 3712 static assert(is(typeof(E().err) == E)); 3713 static assert(is(typeof(new Exception("str").err) == Exception)); 3714 } 3715 3716 /// Strips out $(LREF Err) wrapper from the type. 3717 template stripErr(T) 3718 { 3719 static if (is(immutable T : immutable Err!U, U)) 3720 alias stripErr = U; 3721 else 3722 alias stripErr = T; 3723 } 3724 3725 /// 3726 version(mir_core_test) 3727 unittest 3728 { 3729 static assert(is(stripErr!Exception == Exception)); 3730 static assert(is(stripErr!string == string)); 3731 static assert(is(stripErr!(Err!string) == string)); 3732 } 3733 3734 /++ 3735 3736 See_also: $(LREF some) and $(LREF none). 3737 3738 Params: 3739 visitors = visitors to $(LREF match) with. 3740 +/ 3741 alias suit(alias filter, visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, true, filter); 3742 3743 /// 3744 version(mir_core_test) 3745 @safe pure nothrow @nogc unittest 3746 { 3747 import std.traits: isDynamicArray, Unqual; 3748 import std.meta: templateNot; 3749 alias V = Variant!(long, int, string, long[], int[]); 3750 alias autoGetElementType = match!( 3751 (string s) => "string", // we override the suit handler below for string 3752 suit!(isDynamicArray, a => Unqual!(typeof(a[0])).stringof), 3753 suit!(templateNot!isDynamicArray, a => Unqual!(typeof(a)).stringof), 3754 ); 3755 assert(autoGetElementType(V(string.init)) == "string"); 3756 assert(autoGetElementType(V((long[]).init)) == "long"); 3757 assert(autoGetElementType(V((int[]).init)) == "int"); 3758 assert(autoGetElementType(V(long.init)) == "long"); 3759 assert(autoGetElementType(V(int.init)) == "int"); 3760 } 3761 3762 /// 3763 version(mir_core_test) 3764 @safe pure nothrow @nogc unittest 3765 { 3766 import std.traits: allSameType; 3767 import std.meta: templateNot; 3768 3769 static struct Asteroid { uint size; } 3770 static struct Spaceship { uint size; } 3771 alias SpaceObject = Variant!(Asteroid, Spaceship); 3772 3773 auto errorMsg = "can't unite an asteroid with a spaceship".err; 3774 3775 alias unite = match!( 3776 suit!(allSameType, (a, b) => typeof(a)(a.size + b.size)), 3777 suit!(templateNot!allSameType, (a, b) => errorMsg), 3778 ); 3779 3780 auto ea = Asteroid(10); 3781 auto es = Spaceship(1); 3782 auto oa = SpaceObject(ea); 3783 auto os = SpaceObject(es); 3784 3785 static assert(is(typeof(unite(oa, oa)) == Variant!(Err!string, Asteroid, Spaceship))); 3786 3787 // Asteroid-Asteroid 3788 assert(unite(ea, ea) == Asteroid(20)); 3789 assert(unite(ea, oa) == Asteroid(20)); 3790 assert(unite(oa, ea) == Asteroid(20)); 3791 assert(unite(oa, oa) == Asteroid(20)); 3792 3793 // Asteroid-Spaceship 3794 assert(unite(ea, es) == errorMsg); 3795 assert(unite(ea, os) == errorMsg); 3796 assert(unite(oa, es) == errorMsg); 3797 assert(unite(oa, os) == errorMsg); 3798 3799 // Spaceship-Asteroid 3800 assert(unite(es, ea) == errorMsg); 3801 assert(unite(es, oa) == errorMsg); 3802 assert(unite(os, ea) == errorMsg); 3803 assert(unite(os, oa) == errorMsg); 3804 3805 // Spaceship-Spaceship 3806 assert(unite(es, es) == Spaceship(2)); 3807 assert(unite(es, os) == Spaceship(2)); 3808 assert(unite(os, es) == Spaceship(2)); 3809 assert(unite(os, os) == Spaceship(2)); 3810 } 3811 3812 private template unwrapErrImpl(alias arg) 3813 { 3814 static if (is(immutable typeof(arg) == immutable Err!V, V)) 3815 auto ref unwrapErrImpl() @property { return arg.value; } 3816 else 3817 alias unwrapErrImpl = arg; 3818 } 3819 3820 private template unwrapErr(alias fun) 3821 { 3822 auto ref unwrapErr(Args...)(auto ref return Args args) 3823 { 3824 import std.meta: staticMap; 3825 import std.format: format; 3826 enum expr = () { 3827 string ret = `fun(`; 3828 foreach(i, T; Args) 3829 { 3830 static if (is(immutable T == immutable Err!V, V)) 3831 ret ~= `args[` ~ i.stringof ~ `].value, `; 3832 else 3833 ret ~= `args[` ~ i.stringof ~ `], `; 3834 } 3835 ret ~= `)`; 3836 return ret; 3837 }(); 3838 return mixin(expr); 3839 } 3840 } 3841 3842 /++ 3843 $(LREF some) is a variant of $(LREF suit) that forces that type of any argument doesn't satisfy $(LREF isErr) template. 3844 3845 $(LREF none) is a variant of $(LREF suit) that forces that type of all arguments satisfy $(LREF isErr) template. The handler automatically strips the $(LREF Err) wrapper. 3846 3847 See_also: $(LREF suit), $(LREF Err), $(LREF isErr), $(LREF isResultVariant), and $(LREF reflectErr). 3848 3849 Params: 3850 visitors = visitors to $(LREF match) with. 3851 +/ 3852 alias some(visitors...) = suit!(allArgumentsIsNotInstanceOfErr, naryFun!visitors); 3853 3854 /// ditto 3855 alias none(visitors...) = suit!(anyArgumentIsInstanceOfErr, unwrapErr!(naryFun!visitors)); 3856 3857 /// 3858 version(mir_core_test) 3859 unittest 3860 { 3861 import mir.conv: to; 3862 3863 alias orElse(alias fun) = visit!(some!"a", none!fun); 3864 alias errToString = orElse!(to!string); 3865 3866 // can any other type including integer enum 3867 @reflectErr 3868 static struct ErrorInfo { 3869 string msg; 3870 auto toString() const { return msg; } 3871 } 3872 3873 alias V = Variant!(Err!string, ErrorInfo, Exception, long, double); 3874 alias R = typeof(errToString(V.init)); 3875 3876 static assert(is(R == Variant!(string, long, double)), R.stringof); 3877 3878 { 3879 V v = 1; 3880 assert(v.isOk); 3881 assert(errToString(v) == 1); 3882 } 3883 3884 { 3885 V v = 1.0; 3886 assert(v.isOk); 3887 assert(errToString(v) == 1.0); 3888 } 3889 3890 { 3891 V v = ErrorInfo("msg"); 3892 assert(!v.isOk); 3893 assert(errToString(v) == "msg"); 3894 } 3895 3896 { 3897 V v = "msg".err; 3898 assert(!v.isOk); 3899 assert(errToString(v) == "msg"); 3900 } 3901 3902 { 3903 V v = new Exception("msg"); enum line = __LINE__; 3904 assert(!v.isOk); 3905 assert(errToString(v) == "object.Exception@" ~ __FILE__ ~ "(" ~ line.stringof ~ "): msg"); 3906 } 3907 } 3908 3909 /++ 3910 Attribute that denotes an error type. Can be used with $(LREF some) and $(LREF none). 3911 3912 See_also: $(LREF Err). 3913 +/ 3914 enum reflectErr; 3915 3916 /++ 3917 Checks if T is a instance of $(LREF Err) or if it is annotated with $(LREF reflectErr). 3918 +/ 3919 template isErr(T) 3920 { 3921 import std.traits: isAggregateType, hasUDA; 3922 static if (is(T == enum) || isAggregateType!T) 3923 { 3924 static if (is(immutable T == immutable Err!V, V)) 3925 { 3926 enum isErr = true; 3927 } 3928 else 3929 static if (hasUDA!(T, reflectErr)) 3930 { 3931 enum isErr = true; 3932 } 3933 else 3934 version (D_Exceptions) 3935 { 3936 enum isErr = is(immutable T : immutable Throwable); 3937 } 3938 else 3939 { 3940 enum isErr = false; 3941 } 3942 } 3943 else 3944 { 3945 enum isErr = false; 3946 } 3947 } 3948 3949 /++ 3950 Checks if T is a Variant with at least one allowed type that satisfy $(LREF isErr) traits. 3951 +/ 3952 template isResultVariant(T) 3953 { 3954 static if (is(immutable T == immutable Algebraic!Types, Types...)) 3955 { 3956 import std.meta: anySatisfy; 3957 enum isResultVariant = anySatisfy!(isErr, Types); 3958 } 3959 else 3960 { 3961 enum isResultVariant = false; 3962 } 3963 } 3964 3965 deprecated("Use isResultVariant instead") alias isErrVariant = isResultVariant; 3966 3967 private template anyArgumentIsInstanceOfErr(Args...) 3968 { 3969 import std.meta: anySatisfy; 3970 enum anyArgumentIsInstanceOfErr = anySatisfy!(isErr, Args); 3971 } 3972 3973 private template allArgumentsIsNotInstanceOfErr(Args...) 3974 { 3975 import std.meta: anySatisfy; 3976 enum allArgumentsIsNotInstanceOfErr = !anySatisfy!(isErr, Args); 3977 } 3978 3979 /++ 3980 Gets subtype of algebraic without types for which $(LREF isErr) is true. 3981 +/ 3982 template SomeVariant(T : Algebraic!Types, Types...) 3983 { 3984 import std.meta: Filter, templateNot; 3985 alias SomeVariant = Algebraic!(Filter!(templateNot!isErr, Types)); 3986 } 3987 3988 /// 3989 @safe pure version(mir_core_test) unittest 3990 { 3991 @reflectErr static struct ErrorS { } 3992 alias V = Variant!(ErrorS, Err!string, long, double, This[]); 3993 static assert(is(SomeVariant!V == Variant!(long, double, This[]))); 3994 } 3995 3996 /++ 3997 Gets subtype of algebraic with types for which $(LREF isErr) is true. 3998 +/ 3999 template NoneVariant(T : Algebraic!Types, Types...) 4000 { 4001 import std.meta: Filter; 4002 alias NoneVariant = Algebraic!(Filter!(isErr, Types)); 4003 } 4004 4005 /// 4006 @safe pure version(mir_core_test) unittest 4007 { 4008 @reflectErr static struct ErrorS { } 4009 alias V = Variant!(ErrorS, Err!string, long, double, This[]); 4010 static assert(is(NoneVariant!V == Variant!(ErrorS, Err!string))); 4011 } 4012 4013 private template withNewLine(alias arg) 4014 { 4015 import std.meta: AliasSeq; 4016 alias withNewLine = AliasSeq!("\n", arg); 4017 } 4018 4019 private noreturn throwMe(T...)(auto ref T args) { 4020 static if (T.length == 1) 4021 enum simpleThrow = is(immutable T[0] : immutable Throwable); 4022 else 4023 enum simpleThrow = false; 4024 static if (simpleThrow) 4025 { 4026 throw args[0].toMutable; 4027 } 4028 else 4029 { 4030 import mir.exception: MirException; 4031 static if (__traits(compiles, { import mir.format: print; })) 4032 { 4033 import std.meta: staticMap; 4034 throw new MirException("assumeOk failure:", staticMap!(withNewLine, args)); 4035 } 4036 else 4037 { 4038 import mir.conv: to; 4039 auto msg = "assumeOk failure:"; 4040 foreach(ref arg; args) 4041 { 4042 msg ~= "\n"; 4043 msg ~= arg.to!string; 4044 } 4045 throw new MirException(msg); 4046 } 4047 } 4048 } 4049 4050 version(D_Exceptions) 4051 /++ 4052 Validates that the result doesn't contain an error value. 4053 4054 Params: 4055 visitor = (compiletime) visitor function. Default value is `naryFun!("", "a")`. 4056 handler = (compiletime) visitor handler to use. Default value is $(LREF match). 4057 Throws: 4058 Throws an exception if at least one parameter passed to 4059 `visitor` satisfies $(LREF isErr) traits. 4060 If there is only one paramter (common case) and its value is `Throwable`, throws it. 4061 Otherwise, _all_ paramters will be printed to the exception message using `mir.format.print`. 4062 +/ 4063 alias assumeOk(alias visitor = naryFun!("", "a"), alias handler = .match) = handler!(some!visitor, none!throwMe); 4064 4065 /// 4066 version(mir_core_test) version(D_Exceptions) 4067 unittest 4068 { 4069 import std.exception: collectExceptionMsg; 4070 import mir.exception: MirException; 4071 4072 alias SingleTypeValue = typeof(assumeOk(Variant!(Exception, long).init)); 4073 static assert(is(SingleTypeValue == long), SingleTypeValue.stringof); 4074 4075 4076 // can any other type including integer enum 4077 @reflectErr 4078 static struct ErrorInfo { 4079 string msg; 4080 auto toString() const { return msg; } 4081 } 4082 4083 alias V = Variant!(Err!string, ErrorInfo, Exception, long, double); 4084 alias R = typeof(assumeOk(V.init)); 4085 4086 static assert(is(R == Variant!(long, double)), R.stringof); 4087 4088 { 4089 V v = 1; 4090 assert(v.isOk); 4091 assert(v.assumeOk == 1); 4092 } 4093 4094 { 4095 V v = 1.0; 4096 assert(v.isOk); 4097 assert(v.assumeOk == 1.0); 4098 } 4099 4100 { 4101 V v = ErrorInfo("msg"); 4102 assert(!v.isOk); 4103 assert(v.assumeOk.collectExceptionMsg == "assumeOk failure:\nmsg"); 4104 } 4105 4106 { 4107 V v = "msg".err; 4108 assert(!v.isOk); 4109 assert(v.assumeOk.collectExceptionMsg == "assumeOk failure:\nmsg"); 4110 } 4111 4112 { 4113 V v = new Exception("msg"); 4114 assert(!v.isOk); 4115 assert(v.assumeOk.collectExceptionMsg == "msg"); 4116 } 4117 } 4118 4119 version(mir_core_test) 4120 unittest 4121 { 4122 static struct RequestToken 4123 { 4124 Variant!(long, string) value; 4125 alias value this; 4126 4127 this(T)(T v) 4128 { 4129 value = typeof(value)(v); 4130 } 4131 } 4132 4133 Variant!(int, RequestToken) v = RequestToken.init; 4134 4135 auto r = v.match!( 4136 (int v) { 4137 return assert(false); 4138 }, 4139 ret => ret 4140 ); 4141 4142 static assert(is(typeof(r) == Variant!(long, string))); 4143 } 4144 4145 package auto trustedAllAttr(T)(scope T t) @trusted 4146 { 4147 import std.traits; 4148 enum attrs = (functionAttributes!T & ~FunctionAttribute.system) 4149 | FunctionAttribute.pure_ 4150 | FunctionAttribute.safe 4151 | FunctionAttribute.nogc 4152 | FunctionAttribute.nothrow_; 4153 return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; 4154 } 4155 4156 private static immutable algebraicMembers = [ 4157 "_is", 4158 "_void", 4159 "AllowedTypes", 4160 "MetaFieldsTypes", 4161 "get", 4162 "isNull", 4163 "kind", 4164 "Kind", 4165 "nullify", 4166 "opAssign", 4167 "opCast", 4168 "opCmp", 4169 "opEquals", 4170 "opPostMove", 4171 "toHash", 4172 "toString", 4173 "trustedGet", 4174 "deserializeFromAsdf", 4175 "deserializeFromIon", 4176 ]; 4177 4178 private template UnqualRec(T) 4179 { 4180 import std.traits: Unqual, isDynamicArray, ForeachType; 4181 static if (isDynamicArray!T) 4182 alias UnqualRec = UnqualRec!(ForeachType!T)[]; 4183 else 4184 alias UnqualRec = Unqual!T; 4185 }