1 /** 2 Implementation of unique pointer `UniquePtr` (alias to `btl.autoptr.rc_ptr.RcPtr` with immutable control block). 3 4 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 5 Authors: Adam Búš 6 */ 7 module btl.autoptr.unique_ptr; 8 9 import btl.internal.allocator; 10 import btl.internal.traits; 11 12 static import btl.autoptr.rc_ptr; 13 import btl.autoptr.common; 14 15 16 17 /** 18 Alias to `btl.autoptr.rc_ptr.dynCast`. 19 */ 20 public alias dynCast = btl.autoptr.rc_ptr.dynCast; 21 22 23 24 /** 25 Alias to `btl.autoptr.rc_ptr.dynCastMove`. 26 */ 27 public alias dynCastMove = btl.autoptr.rc_ptr.dynCastMove; 28 29 30 31 /** 32 Alias to `btl.autoptr.rc_ptr.first`. 33 */ 34 public alias first = btl.autoptr.rc_ptr.first; 35 36 37 38 /** 39 Alias to `btl.autoptr.rc_ptr.share`. 40 */ 41 public alias share = btl.autoptr.rc_ptr.share; 42 43 44 45 /** 46 Check if type `T` is `UniquePtr`. 47 */ 48 public template isUniquePtr(T){ 49 import std.traits : isInstanceOf; 50 51 static if(isRcPtr!T) 52 enum bool isUniquePtr = is(T.ControlType == immutable); //is(GetControlType!T == immutable); 53 else 54 enum bool isUniquePtr = false; 55 } 56 57 58 59 /** 60 `UniquePtr` is a smart pointer that owns and manages object through a pointer and disposes of that object when the `UniquePtr` goes out of scope. 61 62 `UniquePtr` is alias to `btl.autoptr.rc_ptr.RcPtr` with immutable `_ControlType`. 63 64 The object is destroyed and its memory deallocated when either of the following happens: 65 66 1. the managing `UniquePtr` object is destroyed 67 68 2. the managing `UniquePtr` object is assigned another pointer via various methods like `opAssign` and `store`. 69 70 The object is destroyed using delete-expression or a custom deleter that is supplied to `UniquePtr` during construction. 71 72 A `UniquePtr` may alternatively own no object, in which case it is called empty. 73 74 Template parameters: 75 76 `_Type` type of managed object 77 78 `_DestructorType` function pointer with attributes of destructor, to get attributes of destructor from type use `btl.autoptr.common.DestructorType!T`. Destructor of type `_Type` must be compatible with `_DestructorType` 79 80 `_ControlType` represent type of counter, must by of type immutable `btl.autoptr.common.ControlBlock`. 81 */ 82 public template UniquePtr( 83 _Type, 84 _DestructorType = DestructorType!_Type, 85 _ControlType = immutable(UniqueControlBlock), 86 ) 87 if(isControlBlock!_ControlType && isDestructorType!_DestructorType){ 88 static assert(is(_ControlType == immutable)); 89 90 alias UniquePtr = btl.autoptr.rc_ptr.RcPtr!(_Type, _DestructorType, _ControlType); 91 } 92 93 /// ditto 94 public template UniquePtr( 95 _Type, 96 _ControlType, 97 _DestructorType = DestructorType!_Type 98 ) 99 if(isControlBlock!_ControlType && isDestructorType!_DestructorType){ 100 static assert(is(_ControlType == immutable)); 101 102 alias UniquePtr = btl.autoptr.rc_ptr.RcPtr!(_Type, _DestructorType, _ControlType); 103 } 104 105 /// 106 unittest{ 107 static class Foo{ 108 int i; 109 110 this(int i)pure nothrow @safe @nogc{ 111 this.i = i; 112 } 113 } 114 115 static class Bar : Foo{ 116 double d; 117 118 this(int i, double d)pure nothrow @safe @nogc{ 119 super(i); 120 this.d = d; 121 } 122 } 123 124 static class Zee : Bar{ 125 bool b; 126 127 this(int i, double d, bool b)pure nothrow @safe @nogc{ 128 super(i, d); 129 this.b = b; 130 } 131 132 ~this()nothrow @system{ 133 } 134 } 135 136 import core.lifetime : move; 137 ///simple: 138 { 139 UniquePtr!long a = UniquePtr!long.make(42); 140 UniquePtr!(const long) b = move(a); 141 assert(a == null); 142 143 assert(*b == 42); 144 assert(b.get == 42); 145 } 146 147 ///polymorphism: 148 { 149 ///create UniquePtr 150 UniquePtr!Foo foo = UniquePtr!Bar.make(42, 3.14); 151 UniquePtr!Zee zee = UniquePtr!Zee.make(42, 3.14, false); 152 153 ///dynamic cast: 154 UniquePtr!Bar bar = dynCastMove!Bar(foo); 155 assert(foo == null); 156 assert(bar != null); 157 158 ///this doesnt work because Foo destructor attributes are more restrictive then Zee's: 159 //UniquePtr!Foo x = move(zee); 160 161 ///this does work: 162 UniquePtr!(Foo, DestructorType!(Foo, Zee)) x = move(zee); 163 assert(zee == null); 164 } 165 166 167 ///multi threading: 168 { 169 ///create SharedPtr with atomic ref counting 170 UniquePtr!(shared Foo) foo = UniquePtr!(shared Bar).make(42, 3.14); 171 172 ///this doesnt work: 173 //foo.get.i += 1; 174 175 import core.atomic : atomicFetchAdd; 176 atomicFetchAdd(foo.get.i, 1); 177 assert(foo.get.i == 43); 178 179 180 ///creating `shared(UniquePtr)`: 181 shared UniquePtr!(shared Bar) bar = share(dynCastMove!Bar(foo)); 182 183 ///`shared(UniquePtr)` is lock free. 184 static assert(typeof(bar).isLockFree == true); 185 186 ///multi thread operations (`store`, `exchange`): 187 UniquePtr!(shared Bar) bar2 = bar.exchange(null); 188 } 189 190 ///dynamic array: 191 { 192 import std.algorithm : all, equal; 193 194 UniquePtr!(long[]) a = UniquePtr!(long[]).make(10, -1); 195 assert(a.length == 10); 196 assert(a.get.length == 10); 197 assert(a.get.all!(x => x == -1)); 198 199 for(int i = 0; i < a.length; ++i){ 200 a.get[i] = i; 201 } 202 assert(a.get[] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 203 } 204 } 205 206 //old 207 pure nothrow @nogc unittest{ 208 209 static class Foo{ 210 int i; 211 212 this(int i)pure nothrow @safe @nogc{ 213 this.i = i; 214 } 215 } 216 217 static class Bar : Foo{ 218 double d; 219 220 this(int i, double d)pure nothrow @safe @nogc{ 221 super(i); 222 this.d = d; 223 } 224 } 225 226 //implicit qualifier cast 227 { 228 UniquePtr!(const Foo) foo = UniquePtr!Foo.make(42); 229 assert(foo.get.i == 42); 230 231 import core.lifetime : move; 232 const UniquePtr!Foo foo2 = foo.move; 233 assert(foo2.get.i == 42); 234 235 } 236 237 //polymorphic classes: 238 { 239 UniquePtr!Foo foo = UniquePtr!Bar.make(42, 3.14); 240 assert(foo != null); 241 assert(foo.get.i == 42); 242 243 //dynamic cast: 244 { 245 UniquePtr!Bar bar = dynCastMove!Bar(foo); 246 assert(foo == null); 247 assert(bar != null); 248 249 assert(bar.get.i == 42); 250 assert(bar.get.d == 3.14); 251 } 252 253 } 254 255 //dynamic array 256 { 257 import std.algorithm : all; 258 259 { 260 auto arr = UniquePtr!(long[]).make(10, -1); 261 262 assert(arr.length == 10); 263 assert(arr.get.all!(x => x == -1)); 264 } 265 266 { 267 auto arr = UniquePtr!(long[]).make(8); 268 assert(arr.length == 8); 269 assert(arr.get.all!(x => x == long.init)); 270 } 271 } 272 273 //static array 274 { 275 import std.algorithm : all; 276 277 { 278 auto arr = UniquePtr!(long[4]).make(-1); 279 assert(arr.get[].all!(x => x == -1)); 280 281 } 282 283 { 284 long[4] tmp = [0, 1, 2, 3]; 285 auto arr = UniquePtr!(long[4]).make(tmp); 286 assert(arr.get[] == tmp[]); 287 } 288 } 289 290 } 291 292 /// 293 pure nothrow @safe @nogc unittest{ 294 //make UniquePtr object 295 static struct Foo{ 296 int i; 297 298 this(int i)pure nothrow @safe @nogc{ 299 this.i = i; 300 } 301 } 302 303 { 304 auto foo = UniquePtr!Foo.make(42); 305 auto foo2 = UniquePtr!Foo.make!Mallocator(42); //explicit stateless allocator 306 } 307 308 { 309 auto arr = UniquePtr!(long[]).make(10); //dynamic array with length 10 310 assert(arr.length == 10); 311 } 312 } 313 314 /// 315 nothrow unittest{ 316 //alloc UniquePtr object 317 import std.experimental.allocator : make, dispose, allocatorObject; 318 319 auto allocator = allocatorObject(Mallocator.instance); 320 321 { 322 auto x = UniquePtr!(long).alloc(allocator, 42); 323 } 324 325 { 326 auto arr = UniquePtr!(long[]).alloc(allocator, 10); //dynamic array with length 10 327 assert(arr.length == 10); 328 } 329 330 } 331 332 333 334 335 336 // make: 337 pure nothrow @safe @nogc unittest{ 338 339 enum bool supportGC = true; 340 341 // 342 { 343 auto s = UniquePtr!long.make(42); 344 } 345 346 { 347 auto s = UniquePtr!long.make!(DefaultAllocator, supportGC)(42); 348 } 349 350 { 351 auto s = UniquePtr!(long, immutable(SharedControlBlock)).make!(DefaultAllocator, supportGC)(42); 352 } 353 354 // dynamic array: 355 { 356 auto s = UniquePtr!(long[]).make(10, 42); 357 assert(s.length == 10); 358 } 359 360 { 361 auto s = UniquePtr!(long[]).make!(DefaultAllocator, supportGC)(10, 42); 362 assert(s.length == 10); 363 } 364 365 { 366 auto s = UniquePtr!(long[], immutable(SharedControlBlock)).make!(DefaultAllocator, supportGC)(10, 42); 367 assert(s.length == 10); 368 } 369 } 370 371 // alloc: 372 nothrow unittest{ 373 import std.experimental.allocator : allocatorObject; 374 375 auto a = allocatorObject(Mallocator.instance); 376 enum bool supportGC = true; 377 378 // 379 { 380 auto s = UniquePtr!long.alloc(a, 42); 381 } 382 383 { 384 auto s = UniquePtr!long.alloc!supportGC(a, 42); 385 } 386 387 { 388 auto s = UniquePtr!(long, immutable(SharedControlBlock)).alloc!supportGC(a, 42); 389 } 390 391 // dynamic array: 392 { 393 auto s = UniquePtr!(long[]).alloc(a, 10, 42); 394 assert(s.length == 10); 395 } 396 397 { 398 auto s = UniquePtr!(long[]).alloc!supportGC(a, 10, 42); 399 assert(s.length == 10); 400 } 401 402 { 403 auto s = UniquePtr!(long[], immutable(SharedControlBlock)).alloc!supportGC(a, 10, 42); 404 assert(s.length == 10); 405 } 406 } 407 408 /// 409 nothrow @nogc unittest{ 410 { 411 auto x = UniquePtr!(shared long).make(123); 412 413 import core.lifetime : move; 414 shared s = share(x.move); 415 assert(x == null); 416 417 auto y = s.exchange(null); 418 assert(*y == 123); 419 } 420 421 { 422 auto x = UniquePtr!(long).make(123); 423 424 ///error `shared UniquePtr` need shared `ControlType` and shared `ElementType`. 425 //shared s = share(x); 426 427 } 428 } 429 430 // 431 pure nothrow @nogc unittest{ 432 import core.lifetime : move; 433 434 { 435 auto x = UniquePtr!(long[]).make(10, -1); 436 assert(x.length == 10); 437 438 auto y = first(x.move); 439 static assert(is(typeof(y) == UniquePtr!long)); 440 assert(*y == -1); 441 } 442 443 { 444 auto x = UniquePtr!(long[10]).make(-1); 445 assert(x.get.length == 10); 446 447 auto y = first(x.move); 448 static assert(is(typeof(y) == UniquePtr!long)); 449 assert(*y == -1); 450 } 451 } 452 453 // 454 pure @safe nothrow @nogc unittest{ 455 static class Foo{ 456 int i; 457 458 this(int i)pure nothrow @safe @nogc{ 459 this.i = i; 460 } 461 } 462 463 static class Bar : Foo{ 464 double d; 465 466 this(int i, double d)pure nothrow @safe @nogc{ 467 super(i); 468 this.d = d; 469 } 470 } 471 472 static class Zee{ 473 } 474 475 { 476 UniquePtr!(const Foo) foo = UniquePtr!Bar.make(42, 3.14); 477 //assert(foo.get.i == 42); 478 479 auto bar = dynCastMove!Bar(foo); 480 assert(bar != null); 481 assert(foo == null); 482 //assert(bar.get.d == 3.14); 483 static assert(is(typeof(bar) == UniquePtr!(const Bar))); 484 485 auto zee = dynCastMove!Zee(bar); 486 assert(zee == null); 487 assert(bar != null); 488 static assert(is(typeof(zee) == UniquePtr!(const Zee))); 489 } 490 } 491 492 493 // 494 unittest{ 495 static class Foo{ 496 int i; 497 498 this(int i)pure nothrow @safe @nogc{ 499 this.i = i; 500 } 501 } 502 503 static class Bar : Foo{ 504 double d; 505 506 this(int i, double d)pure nothrow @safe @nogc{ 507 super(i); 508 this.d = d; 509 } 510 } 511 512 static class Zee{ 513 } 514 515 { 516 import core.lifetime : move; 517 518 UniquePtr!(const Foo) foo = UniquePtr!Bar.make(42, 3.14); 519 assert(foo.get.i == 42); 520 521 auto bar = dynCast!Bar(foo.move); 522 assert(bar != null); 523 assert(foo == null); 524 assert(bar.get.d == 3.14); 525 static assert(is(typeof(bar) == UniquePtr!(const Bar))); 526 527 auto zee = dynCast!Zee(bar.move); 528 assert(zee == null); 529 assert(bar == null); 530 static assert(is(typeof(zee) == UniquePtr!(const Zee))); 531 } 532 } 533 534 535 version(unittest){ 536 537 538 //this null 539 pure nothrow @nogc unittest{ 540 UniquePtr!long x = null; 541 542 assert(x == null); 543 assert(x == UniquePtr!long.init); 544 } 545 546 //this rhs 547 pure nothrow @nogc unittest{ 548 //TODO 549 } 550 551 552 //opAssign null 553 pure nothrow @nogc unittest{ 554 555 { 556 UniquePtr!long x = UniquePtr!long.make(1); 557 558 assert(x != null); 559 assert(*x == 1); 560 x = null; 561 assert(x == null); 562 } 563 564 { 565 UniquePtr!(shared long) x = UniquePtr!(shared long).make(1); 566 567 assert(x != null); 568 assert(*x == 1); 569 x = null; 570 assert(x == null); 571 } 572 } 573 574 //opAssign UniquePtr 575 pure nothrow @nogc unittest{ 576 577 import core.lifetime : move; 578 { 579 UniquePtr!long px1 = UniquePtr!long.make(1); 580 UniquePtr!long px2 = UniquePtr!long.make(2); 581 582 px1 = move(px2); 583 assert(px2 == null); 584 assert(px1 != null); 585 assert(*px1 == 2); 586 } 587 588 589 { 590 UniquePtr!long px = UniquePtr!long.make(1); 591 UniquePtr!(const long) pcx = UniquePtr!long.make(2); 592 593 pcx = move(px); 594 assert(px == null); 595 assert(pcx != null); 596 assert(*pcx == 1); 597 598 } 599 } 600 601 //store 602 nothrow @nogc unittest{ 603 //null store: 604 { 605 shared x = UniquePtr!(shared long).make(123); 606 //*x == 123 607 608 x.store(null); 609 //x is null 610 } 611 612 //rvalue store: 613 { 614 shared x = UniquePtr!(shared long).make(123); 615 //*x == 123 616 617 x.store(UniquePtr!(shared long).make(42)); 618 //*x == 42 619 620 auto tmp = x.exchange(null); 621 //x is null 622 assert(tmp.get == 42); 623 } 624 } 625 626 //exchange 627 pure nothrow @nogc unittest{ 628 import core.lifetime : move; 629 630 { 631 shared UniquePtr!(shared long) x = UniquePtr!(shared long).make(123); 632 UniquePtr!(shared long) y = UniquePtr!(shared long).make(42); 633 634 auto z = x.exchange(y.move); 635 assert(y == null); 636 assert(*z == 123); 637 638 auto tmp = x.exchange(null); 639 assert(*tmp == 42); 640 } 641 642 //swap: 643 { 644 shared UniquePtr!(shared long) x = UniquePtr!(shared long).make(123); 645 UniquePtr!(shared long) y = UniquePtr!(shared long).make(42); 646 647 y = x.exchange(y.move); 648 assert(*y == 123); 649 650 auto tmp = x.exchange(null); 651 assert(*tmp == 42); 652 } 653 } 654 655 //make 656 pure nothrow @nogc unittest{ 657 658 { 659 UniquePtr!long a = UniquePtr!long.make(); 660 assert(a.get == 0); 661 662 UniquePtr!(const long) b = UniquePtr!long.make(2); 663 assert(b.get == 2); 664 } 665 666 { 667 static struct Struct{ 668 int i = 7; 669 670 this(int i)pure nothrow @safe @nogc{ 671 this.i = i; 672 } 673 } 674 675 UniquePtr!Struct s1 = UniquePtr!Struct.make(); 676 assert(s1.get.i == 7); 677 678 UniquePtr!Struct s2 = UniquePtr!Struct.make(123); 679 assert(s2.get.i == 123); 680 } 681 } 682 683 //make dynamic array 684 pure nothrow @nogc unittest{ 685 auto arr = UniquePtr!(long[]).make(6, -1); 686 assert(arr.length == 6); 687 assert(arr.get.length == 6); 688 689 import std.algorithm : all; 690 assert(arr.get.all!(x => x == -1)); 691 692 for(int i = 0; i < 6; ++i) 693 arr.get[i] = i; 694 695 assert(arr.get == [0, 1, 2, 3, 4, 5]); 696 } 697 698 //alloc 699 nothrow unittest{ 700 import std.experimental.allocator : allocatorObject; 701 auto a = allocatorObject(Mallocator.instance); 702 { 703 auto x = UniquePtr!long.alloc(a); 704 assert(x.get == 0); 705 706 auto y = UniquePtr!(const long).alloc(a, 2); 707 assert(y.get == 2); 708 } 709 710 { 711 static struct Struct{ 712 int i = 7; 713 714 this(int i)pure nothrow @safe @nogc{ 715 this.i = i; 716 } 717 } 718 719 auto s1 = UniquePtr!Struct.alloc(a); 720 assert(s1.get.i == 7); 721 722 auto s2 = UniquePtr!Struct.alloc(a, 123); 723 assert(s2.get.i == 123); 724 } 725 } 726 727 //alloc dynamic array 728 nothrow unittest{ 729 import std.experimental.allocator : allocatorObject; 730 auto a = allocatorObject(Mallocator.instance); 731 { 732 auto x = UniquePtr!long.alloc(a); 733 assert(x.get == 0); 734 735 auto y = UniquePtr!(const long).alloc(a, 2); 736 assert(y.get == 2); 737 } 738 739 { 740 static struct Struct{ 741 int i = 7; 742 743 this(int i)pure nothrow @safe @nogc{ 744 this.i = i; 745 } 746 } 747 748 auto s1 = UniquePtr!Struct.alloc(a); 749 assert(s1.get.i == 7); 750 751 auto s2 = UniquePtr!Struct.alloc(a, 123); 752 assert(s2.get.i == 123); 753 } 754 } 755 756 //proxySwap 757 pure nothrow @nogc unittest{ 758 { 759 UniquePtr!long a = UniquePtr!long.make(1); 760 UniquePtr!long b = UniquePtr!long.make(2); 761 a.proxySwap(b); 762 assert(*a == 2); 763 assert(*b == 1); 764 import std.algorithm : swap; 765 swap(a, b); 766 assert(*a == 1); 767 assert(*b == 2); 768 } 769 } 770 771 //opUnary : '*' 772 pure nothrow @nogc unittest{ 773 import core.lifetime : move; 774 775 UniquePtr!long x = UniquePtr!long.make(123); 776 assert(*x == 123); 777 (*x = 321); 778 assert(*x == 321); 779 const y = move(x); 780 assert(*y == 321); 781 assert(x == null); 782 static assert(is(typeof(*y) == const long)); 783 } 784 785 //get 786 pure nothrow @nogc unittest{ 787 import core.lifetime : move; 788 789 UniquePtr!long x = UniquePtr!long.make(123); 790 assert(x.get == 123); 791 x.get = 321; 792 assert(x.get == 321); 793 const y = move(x); 794 assert(y.get == 321); 795 assert(x == null); 796 static assert(is(typeof(y.get) == const long)); 797 } 798 799 //element 800 pure nothrow @nogc unittest{ 801 import core.lifetime : move; 802 803 UniquePtr!long x = UniquePtr!long.make(123); 804 assert(*x.element == 123); 805 x.get = 321; 806 assert(*x.element == 321); 807 const y = move(x); 808 assert(*y.element == 321); 809 assert(x == null); 810 static assert(is(typeof(y.element) == const(long)*)); 811 } 812 813 //length 814 pure nothrow @nogc unittest{ 815 auto x = UniquePtr!(int[]).make(10, -1); 816 assert(x.length == 10); 817 assert(x.get.length == 10); 818 819 import std.algorithm : all; 820 assert(x.get.all!(i => i == -1)); 821 } 822 823 //opCast : bool 824 /+TODO 825 pure nothrow @nogc unittest{ 826 827 UniquePtr!long x = UniquePtr!long.make(123); 828 assert(cast(bool)x); //explicit cast 829 assert(x); //implicit cast 830 x = null; 831 assert(!cast(bool)x); //explicit cast 832 assert(!x); //implicit cast 833 } 834 +/ 835 836 837 //opEquals 838 pure nothrow @nogc unittest{ 839 840 { 841 UniquePtr!long x = UniquePtr!long.make(0); 842 assert(x != null); 843 x = null; 844 assert(x == null); 845 } 846 847 { 848 UniquePtr!long x = UniquePtr!long.make(123); 849 UniquePtr!long y = UniquePtr!long.make(123); 850 assert(x == x); 851 assert(y == y); 852 assert(x != y); 853 } 854 855 { 856 UniquePtr!long x; 857 UniquePtr!(const long) y; 858 assert(x == x); 859 assert(y == y); 860 assert(x == y); 861 } 862 863 { 864 UniquePtr!long x = UniquePtr!long.make(123); 865 UniquePtr!long y = UniquePtr!long.make(123); 866 assert(x == x.element); 867 assert(y.element == y); 868 assert(x != y.element); 869 } 870 } 871 872 //opCmp 873 pure nothrow @nogc unittest{ 874 { 875 const a = UniquePtr!long.make(42); 876 const b = UniquePtr!long.make(123); 877 const n = UniquePtr!long.init; 878 879 assert((a < b) == !(a >= b)); 880 assert((a > b) == !(a <= b)); 881 882 assert(a > n); 883 assert(n < a); 884 } 885 886 { 887 const a = UniquePtr!long.make(42); 888 const b = UniquePtr!long.make(123); 889 890 assert((a < b.element) == !(a.element >= b)); 891 assert((a > b.element) == !(a.element <= b)); 892 } 893 } 894 895 //toHash 896 pure nothrow @nogc unittest{ 897 import core.lifetime : move; 898 { 899 UniquePtr!long x = UniquePtr!long.make(123); 900 UniquePtr!long y = UniquePtr!long.make(123); 901 assert(x.toHash == x.toHash); 902 assert(y.toHash == y.toHash); 903 assert(x.toHash != y.toHash); 904 905 const x_hash = x.toHash; 906 UniquePtr!(const long) z = move(x); 907 assert(x_hash == z.toHash); 908 } 909 { 910 UniquePtr!long x; 911 UniquePtr!(const long) y; 912 assert(x.toHash == x.toHash); 913 assert(y.toHash == y.toHash); 914 assert(x.toHash == y.toHash); 915 } 916 } 917 918 919 }