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