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