1 module nxt.variant; 2 3 version(none): 4 5 import nxt.dbgio : dbg; 6 7 @safe pure: 8 9 /** Lightweight version of $(D std.variant.Algebraic) that doesn't rely on `TypeInfo`. 10 * 11 * Member functions are, when possible, `@safe pure nothrow @nogc`. 12 * 13 * Storage (packing) is more space-efficient. 14 * 15 * TODO: support implicit conversions of (un)signed integer type to larger type 16 * See_Also: https://forum.dlang.org/post/jfasmgwoffmbtuvrtxey@forum.dlang.org 17 * 18 * TODO: add warnings about combining byte, short, int, long, etc. 19 * TODO: add warnings about combining ubyte, ushort, uint, ulong, etc. 20 * 21 * TODO: Use 22 * 23 * align(1) 24 * struct Unaligned 25 * { 26 * align(1): 27 * ubyte filler; 28 * Victim* p; 29 * } 30 * 31 * See_Also: http://forum.dlang.org/post/osfrjcuabwscvrecuvre@forum.dlang.org 32 * See_Also: https://forum.dlang.org/post/jfasmgwoffmbtuvrtxey@forum.dlang.org 33 * See_Also: https://forum.dlang.org/post/tdviqyzrcpttwwnvlzpv@forum.dlang.org 34 * See_Also: https://issues.dlang.org/show_bug.cgi?id=15399 35 */ 36 struct Algebraic(Types...) 37 { 38 @safe: 39 40 alias Ix = ubyte; // type index type. TODO: use uint or size_t when there is room (depending on `memoryPacked`) 41 enum maxTypesCount = 2^^(Ix.sizeof * 8) - 1; // maximum number of allowed type parameters 42 43 import core.internal.traits : Unqual; // TODO: remove by using Andreis trick with `immutable` qualifier 44 import std.meta : anySatisfy, allSatisfy, staticIndexOf; 45 import std.traits : StdCommonType = CommonType, hasIndirections, hasAliasing; 46 import nxt.traits_ex : isComparable, isEquable, sizesOf, stringsOf, allSame; 47 48 public: 49 50 enum name = Algebraic.stringof; 51 alias CommonType = StdCommonType!Types; 52 enum hasCommonType = !is(CommonType == void); 53 54 enum typeCount = Types.length; 55 enum typeSizes = sizesOf!Types; 56 enum typeNames = stringsOf!Types; 57 58 /// Is `true` iff `this` may have aliasing through any of `Types`. 59 enum mayHaveAliasing = anySatisfy!(hasAliasing, Types); 60 61 immutable static typeNamesRT = [typeNames]; // typeNames accessible at run-time, because `[typeNames]` is not @nogc 62 63 /// Is $(D true) if all $(D Types) stored in this $(D Algebraic) has the same length. 64 enum hasFixedSize = allSame!typeSizes; 65 66 private enum N = typeCount; // useful local shorthand 67 68 private enum indexOf(T) = staticIndexOf!(T, Types); // TODO: cast to ubyte if N is <= 256 69 70 // static checking 71 static assert(N >= 1, "No use storing zero types in a " ~ name); 72 static assert(N < maxTypesCount, 73 "Cannot store more than " ~ maxTypesCount.stringof ~ " Types in a " ~ name); 74 75 /** Is `true` if `U` is allowed to be assigned to `this`. */ 76 enum bool allowsAssignmentFrom(U) = ((N == 0 || 77 indexOf!(U) >= 0 || // either direct match or 78 ((!hasIndirections!U) && // no indirections and 79 indexOf!(Unqual!U) >= 0))); // ok to remove constness of value types 80 81 import nxt.maxsize_trait : maxSizeOf; 82 83 enum dataMaxSize = maxSizeOf!Types; 84 85 auto ref to(U)() const // TODO: pure @nogc 86 { 87 import std.conv : to; 88 final switch (typeIndex) 89 { 90 foreach (const i, T; Types) 91 { 92 case i: return as!T.to!U; 93 } 94 } 95 } 96 97 void toString(Sink)(ref scope Sink sink) const /* template-lazy. TODO: pure */ 98 { 99 import std.conv : to; 100 if (!hasValue) 101 return sink("<Uninitialized Algebraic>"); 102 final switch (typeIndex) 103 { 104 foreach (const i, T; Types) 105 { 106 case i: 107 // TODO: use instead to avoid allocations 108 // import std.format : formatValue; 109 // formatValue(sink, as!T); 110 sink(to!string(as!T)); 111 return; 112 } 113 } 114 } 115 116 /** Returns: $(D this) as a HTML-tagged $(D string). */ 117 @property void toHTML(Sink)(ref scope Sink sink) const /* template-lazy. TODO: pure */ 118 { 119 // wrap information in HTML tags with CSS propertie 120 immutable tag = `dlang-` ~ typeName; 121 sink(`<`); sink(tag); sink(`>`); 122 toString(sink); 123 sink(`</`); sink(tag); sink(`>`); 124 } 125 126 pure: 127 128 /** Returns: Name (as a $(D string)) of Currently Stored Type. */ 129 private auto ref typeName()() const @safe nothrow @nogc /* template-lazy */ 130 { 131 pragma(inline, true); 132 return hasValue ? typeNamesRT[typeIndex] : null; 133 } 134 135 /** Copy construct from `that`. */ 136 this()(in Algebraic that) @safe nothrow @nogc 137 { 138 _store = that._store; 139 _tix = that._tix; 140 pragma(msg, "Run postblits for " ~ Types.stringof); 141 } 142 143 /// Destruct. 144 ~this() nothrow @nogc 145 { 146 pragma(inline, true); 147 if (hasValue) 148 release(); 149 } 150 151 /// Construct copy from `that`. 152 this(T)(T that) @trusted nothrow @nogc 153 if (allowsAssignmentFrom!T) 154 { 155 import core.lifetime : moveEmplace; 156 157 alias MT = Unqual!T; 158 static if (__traits(isPOD, MT)) 159 *cast(MT*)(&_store) = that; 160 else 161 moveEmplace(that, *cast(MT*)(&_store)); // TODO: ok when `that` has indirections? 162 163 _tix = cast(Ix)(indexOf!MT + 1); // set type tag 164 } 165 166 Algebraic opAssign(T)(T that) @trusted nothrow @nogc 167 if (allowsAssignmentFrom!T) 168 { 169 import core.lifetime : moveEmplace; 170 171 if (hasValue) 172 release(); 173 174 alias MT = Unqual!T; 175 static if (__traits(isPOD, MT)) 176 *cast(MT*)(&_store) = that; 177 else 178 moveEmplace(that, *cast(MT*)(&_store)); // TODO: ok when `that` has indirections? 179 180 _tix = cast(Ix)(indexOf!MT + 1); // set type tag 181 182 return this; 183 } 184 185 /** If the $(D Algebraic) object holds a value of the $(I exact) type $(D T), 186 returns a pointer to that value. Otherwise, returns $(D null). In cases 187 where $(D T) is statically disallowed, $(D peek) will not compile. 188 */ 189 @property inout(T)* peek(T)() inout @trusted nothrow @nogc 190 { 191 pragma(inline, true); 192 alias MT = Unqual!T; 193 static if (!is(MT == void)) 194 static assert(allowsAssignmentFrom!MT, "Cannot store a " ~ MT.stringof ~ " in a " ~ name); 195 if (!ofType!MT) 196 return null; 197 return cast(inout MT*)&_store; // TODO: alignment 198 } 199 200 /// Get Value of type $(D T). 201 @property auto ref inout(T) get(T)() inout @trusted 202 { 203 version(LDC) pragma(inline, true); // DMD cannot inline 204 if (!ofType!T) 205 throw new AlgebraicException("Algebraic doesn't contain type"); 206 return as!T; 207 } 208 209 /// ditto 210 @property inout(Types[index]) get(uint index)() inout @safe 211 if (index < Types.length) 212 { 213 pragma(inline, true); 214 return get!(Types[index]); 215 } 216 217 /** Interpret data as type $(D T). 218 * 219 * See_Also: https://forum.dlang.org/post/thhrulbqsxbtzoyojqwx@forum.dlang.org 220 */ 221 private @property auto ref inout(T) as(T)() inout @system nothrow @nogc 222 { 223 static if (_store.alignof >= T.alignof) 224 return *(cast(T*)&_store); 225 else 226 { 227 inout(T) result; 228 (cast(ubyte*)&result)[0 .. T.sizeof] = _store[0 .. T.sizeof]; 229 return result; 230 } 231 } 232 233 /// Returns: $(D true) iff $(D this) $(D Algebraic) can store an instance of $(D T). 234 bool ofType(T)() const @safe nothrow @nogc // TODO: shorter name such `isA`, `ofType` 235 { 236 pragma(inline, true); 237 return _tix == indexOf!T + 1; 238 } 239 alias canStore = ofType; 240 241 /// Force $(D this) to the null/uninitialized/unset/undefined state. 242 void clear() @safe nothrow @nogc 243 { 244 pragma(inline, true); 245 if (_tix != _tix.init) 246 { 247 release(); 248 _tix = _tix.init; // this is enough to indicate undefined, no need to zero `_store` 249 } 250 } 251 /// ditto 252 alias nullify = clear; // compatible with `std.typecons.Nullable` 253 254 /// Nullable type support. 255 static immutable nullValue = typeof(this).init; 256 257 /// ditto 258 void opAssign(typeof(null)) 259 { 260 pragma(inline, true); 261 clear(); 262 } 263 264 /// Release internal store. 265 private void release() @trusted nothrow @nogc 266 { 267 import core.internal.traits : hasElaborateDestructor; 268 final switch (typeIndex) 269 { 270 foreach (const i, T; Types) 271 { 272 case i: 273 static if (hasElaborateDestructor!T) 274 .destroy(*cast(T*)&_store); // reinterpret 275 return; 276 } 277 case Ix.max: 278 return; 279 } 280 // TODO: don't call if all types satisfy traits_ex.isValueType 281 // _store[] = 0; // slightly faster than: memset(&_store, 0, _store.sizeof); 282 } 283 284 /// Returns: $(D true) if this has a defined value (is defined). 285 bool hasValue() const @safe nothrow @nogc 286 { 287 pragma(inline, true); 288 return _tix != _tix.init; 289 } 290 291 bool isNull() const @safe nothrow @nogc 292 { 293 pragma(inline, true); 294 return _tix == _tix.init; 295 } 296 297 size_t currentSize()() const @safe nothrow @nogc /* template-lazy */ 298 { 299 if (isNull) 300 return 0; 301 final switch (typeIndex) 302 { 303 foreach (const i, const typeSize; typeSizes) 304 { 305 case i: 306 return typeSize; 307 } 308 } 309 } 310 311 /// Blindly Implicitly Convert Stored Value in $(D U). 312 private U convertTo(U)() const @trusted nothrow 313 { 314 assert(hasValue); 315 final switch (typeIndex) 316 { 317 foreach (const i, T; Types) 318 { 319 case i: 320 return as!T; 321 } 322 } 323 } 324 325 static if (hasCommonType) 326 { 327 CommonType commonValue() const @trusted pure nothrow @nogc 328 { 329 assert(hasValue); 330 final switch (typeIndex) 331 { 332 foreach (const i, T; Types) 333 { 334 case i: 335 return cast(CommonType)as!T; 336 } 337 } 338 } 339 } 340 341 static if (allSatisfy!(isEquable, Types)) 342 { 343 static if (hasCommonType) 344 { 345 bool opEquals()(in Algebraic that) const @trusted nothrow @nogc /* template-lazy, opEquals is nothrow @nogc */ 346 { 347 if (_tix != that._tix) 348 return (this.convertTo!CommonType == 349 that.convertTo!CommonType); 350 if (!this.hasValue && 351 !that.hasValue) 352 return true; // TODO: same behaviour as floating point NaN? 353 final switch (typeIndex) 354 { 355 foreach (const i, T; Types) 356 { 357 case i: 358 return this.as!T == that.as!T; 359 } 360 } 361 } 362 } 363 else 364 { 365 bool opEquals()(in Algebraic that) const @trusted nothrow /* template-lazy */ 366 { 367 if (_tix != that._tix) 368 return false; // this needs to be nothrow or otherwise x in aa will throw which is not desirable 369 370 if (!this.hasValue && 371 !that.hasValue) 372 return true; // TODO: same behaviour as floating point NaN? 373 374 final switch (typeIndex) 375 { 376 foreach (const i, T; Types) 377 { 378 case i: 379 return (this.as!T == 380 that.as!T); 381 } 382 } 383 384 assert(false); // this is for knet to compile but not in this module. TODO: remove when compiler is fixed 385 } 386 } 387 388 bool opEquals(T)(in T that) const @trusted nothrow 389 { 390 // TODO: assert failure only if none of the Types isComparable to T 391 static assert (allowsAssignmentFrom!T, 392 "Cannot equal any possible type of " ~ Algebraic.stringof ~ 393 " with " ~ T.stringof); 394 395 if (!ofType!T) 396 return false; // throw new AlgebraicException("Cannot equal Algebraic with current type " ~ "[Types][typeIndex]" ~ " with different types " ~ "T.stringof"); 397 return (this.as!T == that); 398 } 399 } 400 401 static if (allSatisfy!(isComparable, Types)) 402 { 403 int opCmp()(in Algebraic that) const @trusted /* template-lazy, TODO: extend to Algebraic!(ThatTypes) */ 404 { 405 static if (hasCommonType) // TODO: extend to haveCommonType!(Types, ThatTypes) 406 { 407 if (_tix != that._tix) 408 { 409 // TODO: functionize to defaultOpCmp to avoid postblits: 410 const a = this.convertTo!CommonType; 411 const b = that.convertTo!CommonType; 412 return a < b ? -1 : a > b ? 1 : 0; 413 } 414 } 415 else 416 { 417 if (_tix != that._tix) 418 throw new AlgebraicException("Cannot compare Algebraic of type " ~ typeNamesRT[typeIndex] ~ 419 " with Algebraic of type " ~ typeNamesRT[that.typeIndex]); 420 } 421 422 final switch (typeIndex) 423 { 424 foreach (const i, T; Types) 425 { 426 case i: 427 // TODO: functionize to defaultOpCmp to avoid postblits: 428 const a = this.as!T; 429 const b = that.as!T; 430 return a < b ? -1 : a > b ? 1 : 0; 431 } 432 } 433 } 434 435 int opCmp(U)(in U that) const @trusted 436 { 437 static if (!is(StdCommonType!(Types, U) == void)) // TODO: is CommonType or isComparable the correct way of checking this? 438 { 439 final switch (typeIndex) 440 { 441 foreach (const i, T; Types) 442 { 443 case i: 444 const a = this.as!T; 445 return a < that ? -1 : a > that ? 1 : 0; // TODO: functionize to defaultOpCmp 446 } 447 } 448 } 449 else 450 { 451 static assert(allowsAssignmentFrom!U, // TODO: relax to allowsComparisonWith!U 452 "Cannot compare " ~ Algebraic.stringof ~ " with " ~ U.stringof); 453 if (!ofType!U) 454 throw new AlgebraicException("Cannot compare " ~ Algebraic.stringof ~ " with " ~ U.stringof); 455 // TODO: functionize to defaultOpCmp to avoid postblits: 456 const a = this.as!U; 457 return a < that ? -1 : a > that ? 1 : 0; 458 } 459 } 460 } 461 462 extern (D) hash_t toHash() const @trusted pure nothrow 463 { 464 import core.internal.hash : hashOf; 465 const typeof(return) hash = _tix.hashOf; 466 if (hasValue) 467 { 468 final switch (typeIndex) 469 { 470 foreach (const i, T; Types) 471 { 472 case i: return as!T.hashOf(hash); 473 } 474 } 475 } 476 return hash; 477 } 478 479 import std.digest : isDigest; 480 481 /// TODO: use `!hasAliasing`? 482 void toDigest(Digest)(scope ref Digest digest) const nothrow @nogc 483 if (isDigest!Digest) 484 { 485 import nxt.digestion : digestAny; 486 digestAny(digest, _tix); 487 if (hasValue) 488 { 489 final switch (typeIndex) 490 { 491 foreach (const i, T; Types) 492 { 493 case i: 494 digestAny(digest, as!T); 495 return; 496 } 497 } 498 } 499 } 500 501 private: 502 // immutable to make hasAliasing!(Algebraic!(...)) false 503 union 504 { 505 //align(8): 506 static if (mayHaveAliasing) 507 { 508 ubyte[dataMaxSize] _store; 509 void* alignDummy; // non-packed means good alignment. TODO: check for maximum alignof of Types 510 } 511 else 512 { 513 // to please hasAliasing!(typeof(this)): 514 immutable(ubyte)[dataMaxSize] _store; 515 immutable(void)* alignDummy; // non-packed means good alignment. TODO: check for maximum alignof of Types 516 } 517 } 518 519 size_t typeIndex() const nothrow @nogc 520 { 521 pragma(inline, true); 522 assert(_tix != 0, "Cannot get index from uninitialized (null) variant."); 523 return _tix - 1; 524 } 525 526 Ix _tix = 0; // type index 527 } 528 529 /// Algebraic type exception. 530 static class AlgebraicException : Exception 531 { 532 this(string s) pure @nogc 533 { 534 super(s); 535 } 536 } 537 538 unittest 539 { 540 // Algebraic!(float, double, bool) a; 541 // a = 2.1; assert(a.to!string == "2.1"); assert(a.toHTML == "<dlang-double>2.1</dlang-double>"); 542 // a = 2.1f; assert(a.to!string == "2.1"); assert(a.toHTML == "<dlang-float>2.1</dlang-float>"); 543 // a = true; assert(a.to!string == "true"); assert(a.toHTML == "<dlang-bool>true</dlang-bool>"); 544 } 545 546 pure: 547 548 /// equality and comparison 549 @trusted nothrow @nogc unittest 550 { 551 Algebraic!(float) a, b; 552 static assert(a.hasFixedSize); 553 554 a = 1.0f; 555 assert(a._tix != a.Ix.init); 556 557 b = 1.0f; 558 assert(b._tix != b.Ix.init); 559 560 assert(a._tix == b._tix); 561 assert((cast(ubyte*)&a)[0 .. a.sizeof] == (cast(ubyte*)&b)[0 .. b.sizeof]); 562 assert(a == b); // TODO: this errors with dmd master 563 } 564 565 /// 566 nothrow @nogc unittest 567 { 568 alias C = Algebraic!(float, double); 569 C a = 1.0; 570 const C b = 2.0; 571 const C c = 2.0f; 572 const C d = 1.0f; 573 574 assert(a.commonValue == 1); 575 assert(b.commonValue == 2); 576 assert(c.commonValue == 2); 577 assert(d.commonValue == 1); 578 579 // nothrow comparison possible 580 assert(a < b); 581 assert(a < c); 582 assert(a == d); 583 584 static assert(!a.hasFixedSize); 585 static assert(a.allowsAssignmentFrom!float); 586 static assert(a.allowsAssignmentFrom!double); 587 static assert(!a.allowsAssignmentFrom!string); 588 589 a.clear(); 590 assert(!a.hasValue); 591 assert(a.peek!float is null); 592 assert(a.peek!double is null); 593 assert(a.currentSize == 0); 594 } 595 596 /// aliasing traits 597 nothrow @nogc unittest 598 { 599 import std.traits : hasAliasing; 600 static assert(!hasAliasing!(Algebraic!(long, double))); 601 static assert(!hasAliasing!(Algebraic!(long, string))); 602 static assert(!hasAliasing!(Algebraic!(long, immutable(double)*))); 603 static assert(hasAliasing!(Algebraic!(long, double*))); 604 } 605 606 nothrow @nogc unittest 607 { 608 alias V = Algebraic!(long, double); 609 const a = V(1.0); 610 611 static assert(a.hasFixedSize); 612 613 assert(a.ofType!double); 614 assert(a.peek!long is null); 615 assert(a.peek!double !is null); 616 617 static assert(is(typeof(a.peek!long) == const(long)*)); 618 static assert(is(typeof(a.peek!double) == const(double)*)); 619 } 620 621 /// equality and comparison 622 nothrow @nogc unittest 623 { 624 Algebraic!(int) a, b; 625 static assert(a.hasFixedSize); 626 a = 1; 627 b = 1; 628 assert(a == b); 629 } 630 631 /// equality and comparison 632 nothrow @nogc unittest 633 { 634 Algebraic!(float) a, b; 635 static assert(a.hasFixedSize); 636 a = 1.0f; 637 b = 1.0f; 638 assert(a == b); 639 } 640 641 /// equality and comparison 642 /*TODO: @nogc*/ unittest 643 { 644 Algebraic!(float, double, string) a, b; 645 646 static assert(!a.hasFixedSize); 647 648 a = 1.0f; 649 b = 1.0f; 650 assert(a == b); 651 652 a = 1.0f; 653 b = 2.0f; 654 assert(a != b); 655 assert(a < b); 656 assert(b > a); 657 658 a = "alpha"; 659 b = "alpha"; 660 assert(a == b); 661 662 a = "a"; 663 b = "b"; 664 assert(a != b); 665 assert(a < b); 666 assert(b > a); 667 } 668 669 /// AA keys 670 nothrow unittest 671 { 672 alias C = Algebraic!(float, double); 673 static assert(!C.hasFixedSize); 674 string[C] a; 675 a[C(1.0f)] = "1.0f"; 676 a[C(2.0)] = "2.0"; 677 assert(a[C(1.0f)] == "1.0f"); 678 assert(a[C(2.0)] == "2.0"); 679 } 680 681 /// verify nothrow comparisons 682 nothrow @nogc unittest 683 { 684 alias C = Algebraic!(int, float, double); 685 static assert(!C.hasFixedSize); 686 assert(C(1.0) < 2); 687 assert(C(1.0) < 2.0); 688 assert(C(1.0) < 2.0); 689 static assert(!__traits(compiles, { C(1.0) < 'a'; })); // cannot compare with char 690 static assert(!__traits(compiles, { C(1.0) < "a"; })); // cannot compare with string 691 } 692 693 /// TODO 694 nothrow @nogc unittest 695 { 696 // alias C = Algebraic!(int, float, double); 697 // alias D = Algebraic!(float, double); 698 // assert(C(1) < D(2.0)); 699 // assert(C(1) < D(1.0)); 700 // static assert(!__traits(compiles, { C(1.0) < "a"; })); // cannot compare with string 701 } 702 703 /// if types have CommonType comparison is nothrow @nogc 704 nothrow @nogc unittest 705 { 706 alias C = Algebraic!(short, int, long, float, double); 707 static assert(!C.hasFixedSize); 708 assert(C(1) != C(2.0)); 709 assert(C(1) == C(1.0)); 710 } 711 712 /// if types have `CommonType` then comparison is `nothrow @nogc` 713 nothrow @nogc unittest 714 { 715 alias C = Algebraic!(short, int, long, float, double); 716 static assert(!C.hasFixedSize); 717 assert(C(1) != C(2.0)); 718 assert(C(1) == C(1.0)); 719 } 720 721 nothrow @nogc unittest 722 { 723 alias C = Algebraic!(int, string); 724 static assert(!C.hasFixedSize); 725 C x; 726 x = 42; 727 } 728 729 nothrow @nogc unittest 730 { 731 alias C = Algebraic!(int); 732 static assert(C.hasFixedSize); 733 C x; 734 x = 42; 735 } 736 737 unittest 738 { 739 import core.internal.traits : hasElaborateCopyConstructor; 740 741 import std.exception : assertThrown; 742 743 static assert(hasElaborateCopyConstructor!(char[2]) == false); 744 static assert(hasElaborateCopyConstructor!(char[]) == false); 745 746 // static assert(Algebraic!(char, wchar).sizeof == 2 + 1); 747 // static assert(Algebraic!(wchar, dchar).sizeof == 4 + 1); 748 // static assert(Algebraic!(long, double).sizeof == 8 + 1); 749 // static assert(Algebraic!(int, float).sizeof == 4 + 1); 750 // static assert(Algebraic!(char[2], wchar[2]).sizeof == 2 * 2 + 1); 751 752 alias C = Algebraic!(string, 753 // fixed length strings: small string optimizations (SSOs) 754 int, float, 755 long, double); 756 static assert(!C.hasFixedSize); 757 758 static assert(C.allowsAssignmentFrom!int); 759 static assert(!C.allowsAssignmentFrom!(int[2])); 760 static assert(C.allowsAssignmentFrom!(const(int))); 761 762 static assert(C.dataMaxSize == string.sizeof); 763 static assert(!__traits(compiles, { assert(d == 'a'); })); 764 765 assert(C() == C()); // two undefined are equal 766 767 C d; 768 C e = d; // copy construction 769 assert(e == d); // two undefined should not equal 770 771 d = 11; 772 assert(d != e); 773 774 // TODO: Allow this d = cast(ubyte)255; 775 776 d = 1.0f; 777 assertThrown!AlgebraicException(d.get!double); 778 assert(d.hasValue); 779 assert(d.ofType!float); 780 assert(d.peek!float !is null); 781 assert(!d.ofType!double); 782 assert(d.peek!double is null); 783 assert(d.get!float == 1.0f); 784 assert(d == 1.0f); 785 assert(d != 2.0f); 786 assert(d < 2.0f); 787 assert(d != "2.0f"); 788 assertThrown!AlgebraicException(d < 2.0); 789 assertThrown!AlgebraicException(d < "2.0"); 790 assert(d.currentSize == float.sizeof); 791 792 d = 2; 793 assert(d.hasValue); 794 assert(d.peek!int !is null); 795 assert(!d.ofType!float); 796 assert(d.peek!float is null); 797 assert(d.get!int == 2); 798 assert(d == 2); 799 assert(d != 3); 800 assert(d < 3); 801 assertThrown!AlgebraicException(d < 2.0f); 802 assertThrown!AlgebraicException(d < "2.0"); 803 assert(d.currentSize == int.sizeof); 804 805 d = "abc"; 806 assert(d.hasValue); 807 assert(d.get!0 == "abc"); 808 assert(d.get!string == "abc"); 809 assert(d.ofType!string); 810 assert(d.peek!string !is null); 811 assert(d == "abc"); 812 assert(d != "abcd"); 813 assert(d < "abcd"); 814 assertThrown!AlgebraicException(d < 2.0f); 815 assertThrown!AlgebraicException(d < 2.0); 816 assert(d.currentSize == string.sizeof); 817 818 d = 2.0; 819 assert(d.hasValue); 820 assert(d.get!double == 2.0); 821 assert(d.ofType!double); 822 assert(d.peek!double !is null); 823 assert(d == 2.0); 824 assert(d != 3.0); 825 assert(d < 3.0); 826 assertThrown!AlgebraicException(d < 2.0f); 827 assertThrown!AlgebraicException(d < "2.0"); 828 assert(d.currentSize == double.sizeof); 829 830 d.clear(); 831 assert(d.peek!int is null); 832 assert(d.peek!float is null); 833 assert(d.peek!double is null); 834 assert(d.peek!string is null); 835 assert(!d.hasValue); 836 assert(d.currentSize == 0); 837 838 assert(C(1.0f) == C(1.0f)); 839 assert(C(1.0f) < C(2.0f)); 840 assert(C(2.0f) > C(1.0f)); 841 842 assertThrown!AlgebraicException(C(1.0f) < C(1.0)); 843 // assertThrown!AlgebraicException(C(1.0f) == C(1.0)); 844 } 845 846 /// 847 nothrow @nogc unittest 848 { 849 import nxt.container.static_array : MutableStringN; 850 alias String15 = MutableStringN!(15); 851 852 String15 s; 853 String15 t = s; 854 assert(t == s); 855 856 alias V = Algebraic!(String15, string); 857 V v = String15("first"); 858 assert(v.peek!String15); 859 assert(!v.peek!string); 860 861 v = String15("second"); 862 assert(v.peek!String15); 863 assert(!v.peek!string); 864 865 v = "third"; 866 assert(!v.peek!String15); 867 assert(v.peek!string); 868 869 auto w = v; 870 assert(v == w); 871 w.clear(); 872 assert(!v.isNull); 873 assert(w.isNull); 874 w = v; 875 assert(!w.isNull); 876 877 v = V.init; 878 assert(v == V.init); 879 } 880 881 /// check default values 882 nothrow @nogc unittest 883 { 884 import nxt.container.static_array : MutableStringN; 885 alias String15 = MutableStringN!(15); 886 887 alias V = Algebraic!(String15, string); 888 V _; 889 assert(_._tix == V.Ix.init); 890 assert(V.init._tix == V.Ix.init); 891 892 // TODO: import nxt.bit_traits : isInitAllZeroBits; 893 // TODO: static assert(isInitAllZeroBits!(V)); 894 }