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