StormByte 2.0.0
C++26 foundation of the StormByte suite
 
Loading...
Searching...
No Matches
safe_pointers.hxx
Go to the documentation of this file.
1/*
2 * Copyright (C) 2024-2026 David C. Manuelda (StormBytePP)
3 *
4 * This file is part of StormByte.
5 *
6 * StormByte original source is dual-licensed:
7 *
8 * 1. GNU Lesser General Public License v3.0 (or later)
9 * You may redistribute and/or modify this file under the terms of the
10 * GNU Lesser General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option)
12 * any later version.
13 *
14 * 2. Commercial license
15 * Alternatively, this file may be used under the terms of a commercial
16 * license agreement with the copyright holder
17 * (David C. Manuelda <StormByte@gmail.com>).
18 *
19 * Both licenses apply only to original StormByte source in this repository.
20 * They do not cover other StormByte modules or any third-party material
21 * shipped with this repository (including everything under thirdparty/),
22 * which remains under its own license.
23 *
24 * Neither license grants any patent rights. Any patent licenses required
25 * to use this software or third-party components must be obtained separately
26 * from the patent holders.
27 *
28 * StormByte is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU Lesser General Public License for more details.
32 *
33 * You should have received a copy of the GNU Lesser General Public License
34 * version 3 along with StormByte. If not, see
35 * <https://www.gnu.org/licenses/lgpl-3.0.html>.
36 *
37 * SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-StormByte-Commercial
38 */
39
40#pragma once
41
44
45#include <compare>
46#include <cstddef>
47#include <functional>
48#include <memory>
49#include <type_traits>
50#include <utility>
51
65namespace StormByte {
66 template<class T>
67 class Shared;
68
69 template<class T>
70 class Unique;
71
72 template<class T>
73 class Weak;
74
82 namespace Heap {
89 STORMBYTE_PUBLIC void* Allocate(std::size_t bytes);
90
95 STORMBYTE_PUBLIC void Free(void* pointer) noexcept;
96
107 template<class U>
108 void operator()(U* pointer) const noexcept {
109 pointer->~U();
110 Free(pointer);
111 }
112 };
113
119 template<typename T>
120 struct Allocator {
121 using value_type = T;
122
126 constexpr Allocator() noexcept = default;
127
132 template<typename U>
133 constexpr Allocator(const Allocator<U>&) noexcept {}
134
140 [[nodiscard]] T* allocate(std::size_t count) {
141 return static_cast<T*>(Allocate(count * sizeof(T)));
142 }
143
148 void deallocate(T* pointer, std::size_t) noexcept {
149 Free(pointer);
150 }
151
157 template<typename U>
158 constexpr bool operator==(const Allocator<U>&) const noexcept {
159 return true;
160 }
161 };
162
170 template<class T, class... Args>
171 Shared<T> MakeShared(Args&&... args);
172
180 template<class T, class... Args>
181 Unique<T> MakeUnique(Args&&... args);
182 }
183
200 template<class T>
201 class Shared {
202 public:
203 using element_type = T;
205
209 Shared() noexcept = default;
210
214 Shared(std::nullptr_t) noexcept {}
215
221 template<class U>
223 Shared(const Shared<U>& other) noexcept: m_ptr(other.m_ptr) {}
224
230 template<class U>
232 Shared(Shared<U>&& other) noexcept: m_ptr(std::move(other.m_ptr)) {}
233
237 Shared(const Shared&) noexcept = default;
238
242 Shared(Shared&&) noexcept = default;
243
249 explicit Shared(const Weak<T>& weak);
250
255 Shared& operator=(const Shared&) noexcept = default;
256
261 Shared& operator=(Shared&&) noexcept = default;
262
266 ~Shared() = default;
267
272 T* get() const noexcept {
273 return m_ptr.get();
274 }
275
280 T& operator*() const {
281 return *m_ptr;
282 }
283
288 T* operator->() const noexcept {
289 return m_ptr.get();
290 }
291
296 explicit operator bool() const noexcept {
297 return static_cast<bool>(m_ptr);
298 }
299
304 operator std::shared_ptr<T>() const noexcept {
305 return m_ptr;
306 }
307
312 long use_count() const noexcept {
313 return m_ptr.use_count();
314 }
315
319 void reset() noexcept {
320 m_ptr.reset();
321 }
322
327 void swap(Shared& other) noexcept {
328 m_ptr.swap(other.m_ptr);
329 }
330
337 template<class U>
338 bool owner_before(const Shared<U>& other) const noexcept {
339 return m_ptr.owner_before(other.m_ptr);
340 }
341
348 template<class U>
349 bool owner_before(const Weak<U>& other) const noexcept;
350
358 template<class Target, class... Args>
360 static Shared<T> MakePointer(Args&&... args) {
361 return Heap::MakeShared<Target>(std::forward<Args>(args)...);
362 }
363
369 friend bool operator==(const Shared& lhs, std::nullptr_t) noexcept {
370 return !lhs.m_ptr;
371 }
372
378 friend bool operator!=(const Shared& lhs, std::nullptr_t) noexcept {
379 return static_cast<bool>(lhs.m_ptr);
380 }
381
387 friend bool operator==(std::nullptr_t, const Shared& rhs) noexcept {
388 return !rhs.m_ptr;
389 }
390
396 friend bool operator!=(std::nullptr_t, const Shared& rhs) noexcept {
397 return static_cast<bool>(rhs.m_ptr);
398 }
399
406 friend bool operator==(const Shared& lhs, const Shared& rhs) noexcept {
407 return lhs.get() == rhs.get();
408 }
409
416 friend auto operator<=>(const Shared& lhs, const Shared& rhs) noexcept {
417 return std::compare_three_way{}(lhs.get(), rhs.get());
418 }
419
425 friend void swap(Shared& left, Shared& right) noexcept {
426 left.swap(right);
427 }
428
429 private:
434 explicit Shared(std::shared_ptr<T> pointer) noexcept: m_ptr(std::move(pointer)) {}
435
439 struct Adopt {
440 explicit constexpr Adopt() noexcept = default;
441 };
442
447 explicit Shared(Adopt, T* object): m_ptr(object, Heap::ObjectDeleter{}, Heap::Allocator<T>{}) {}
448
449 template<class U>
450 friend class Shared;
451
452 template<class U>
453 friend class Weak;
454
455 template<class U, class... Args>
456 friend Shared<U> Heap::MakeShared(Args&&...);
457
458 template<class X, class Y>
459 friend Shared<X> StaticPointerCast(const Shared<Y>&) noexcept;
460
461 template<class X, class Y>
462 friend Shared<X> DynamicPointerCast(const Shared<Y>&) noexcept;
463
464 template<class X, class Y>
465 friend Shared<X> ConstPointerCast(const Shared<Y>&) noexcept;
466
467 template<class X, class Y>
469
470 std::shared_ptr<T> m_ptr;
471 };
472
489 template<class T>
490 class Unique {
491 public:
492 using element_type = T;
493 using pointer = T*;
495
499 Unique() noexcept = default;
500
504 Unique(std::nullptr_t) noexcept {}
505
511 template<class U>
512 requires Type::SameAs<U, T> || (Type::DerivedFrom<U, T> && std::has_virtual_destructor_v<T>)
513 Unique(Unique<U>&& other) noexcept: m_ptr(std::move(other.m_ptr)) {}
514
518 Unique(const Unique&) = delete;
519
523 Unique(Unique&&) noexcept = default;
524
529 Unique& operator=(const Unique&) = delete;
530
535 Unique& operator=(Unique&&) noexcept = default;
536
540 ~Unique() = default;
541
546 T* get() const noexcept {
547 return m_ptr.get();
548 }
549
554 T& operator*() const {
555 return *m_ptr;
556 }
557
562 T* operator->() const noexcept {
563 return m_ptr.get();
564 }
565
570 explicit operator bool() const noexcept {
571 return static_cast<bool>(m_ptr);
572 }
573
578 operator std::unique_ptr<T, Heap::ObjectDeleter>() && noexcept {
579 return std::move(m_ptr);
580 }
581
587 return m_ptr.get_deleter();
588 }
589
594 const deleter_type& get_deleter() const noexcept {
595 return m_ptr.get_deleter();
596 }
597
601 void reset() noexcept {
602 m_ptr.reset();
603 }
604
609 void swap(Unique& other) noexcept {
610 m_ptr.swap(other.m_ptr);
611 }
612
621 template<class Target, class... Args>
622 requires Type::SameAs<Target, T> || (Type::DerivedFrom<Target, T> && std::has_virtual_destructor_v<T>)
623 static Unique<T> MakePointer(Args&&... args) {
624 return Heap::MakeUnique<Target>(std::forward<Args>(args)...);
625 }
626
632 friend bool operator==(const Unique& lhs, std::nullptr_t) noexcept {
633 return !lhs.m_ptr;
634 }
635
641 friend bool operator!=(const Unique& lhs, std::nullptr_t) noexcept {
642 return static_cast<bool>(lhs.m_ptr);
643 }
644
650 friend bool operator==(std::nullptr_t, const Unique& rhs) noexcept {
651 return !rhs.m_ptr;
652 }
653
659 friend bool operator!=(std::nullptr_t, const Unique& rhs) noexcept {
660 return static_cast<bool>(rhs.m_ptr);
661 }
662
669 friend bool operator==(const Unique& lhs, const Unique& rhs) noexcept {
670 return lhs.get() == rhs.get();
671 }
672
679 friend auto operator<=>(const Unique& lhs, const Unique& rhs) noexcept {
680 return std::compare_three_way{}(lhs.get(), rhs.get());
681 }
682
688 friend void swap(Unique& left, Unique& right) noexcept {
689 left.swap(right);
690 }
691
692 private:
696 struct Adopt {
697 explicit constexpr Adopt() noexcept = default;
698 };
699
704 explicit Unique(Adopt, T* object): m_ptr(object) {}
705
706 template<class U>
707 friend class Unique;
708
709 template<class U, class... Args>
710 friend Unique<U> Heap::MakeUnique(Args&&...);
711
712 std::unique_ptr<T, Heap::ObjectDeleter> m_ptr;
713 };
714
724 template<class T>
725 class Weak {
726 public:
727 using element_type = T;
728
732 Weak() noexcept = default;
733
737 Weak(std::nullptr_t) noexcept {}
738
744 template<class U>
746 Weak(const Shared<U>& owner) noexcept: m_weak(owner.m_ptr) {}
747
751 Weak(const Weak&) noexcept = default;
752
756 Weak(Weak&&) noexcept = default;
757
762 Weak& operator=(const Weak&) noexcept = default;
763
768 Weak& operator=(Weak&&) noexcept = default;
769
773 ~Weak() = default;
774
779 Shared<T> lock() const noexcept {
780 return Shared<T>(m_weak.lock());
781 }
782
787 bool expired() const noexcept {
788 return m_weak.expired();
789 }
790
795 long use_count() const noexcept {
796 return m_weak.use_count();
797 }
798
802 void reset() noexcept {
803 m_weak.reset();
804 }
805
810 void swap(Weak& other) noexcept {
811 m_weak.swap(other.m_weak);
812 }
813
820 template<class U>
821 bool owner_before(const Weak<U>& other) const noexcept {
822 return m_weak.owner_before(other.m_weak);
823 }
824
831 template<class U>
832 bool owner_before(const Shared<U>& other) const noexcept {
833 return m_weak.owner_before(other.m_ptr);
834 }
835
841 friend void swap(Weak& left, Weak& right) noexcept {
842 left.swap(right);
843 }
844
845 private:
846 template<class U>
847 friend class Weak;
848
849 template<class U>
850 friend class Shared;
851
852 std::weak_ptr<T> m_weak;
853 };
854
862 template<class T, class U>
863 Shared<T> StaticPointerCast(const Shared<U>& from) noexcept {
864 return Shared<T>(std::static_pointer_cast<T>(std::shared_ptr<U>(from)));
865 }
866
874 template<class T, class U>
875 Shared<T> DynamicPointerCast(const Shared<U>& from) noexcept {
876 return Shared<T>(std::dynamic_pointer_cast<T>(std::shared_ptr<U>(from)));
877 }
878
886 template<class T, class U>
887 Shared<T> ConstPointerCast(const Shared<U>& from) noexcept {
888 return Shared<T>(std::const_pointer_cast<T>(std::shared_ptr<U>(from)));
889 }
890
898 template<class T, class U>
900 return Shared<T>(std::reinterpret_pointer_cast<T>(std::shared_ptr<U>(from)));
901 }
902
903 template<class T>
904 Shared<T>::Shared(const Weak<T>& weak): m_ptr(weak.m_weak) {}
905
906 template<class T>
907 template<class U>
908 bool Shared<T>::owner_before(const Weak<U>& other) const noexcept {
909 return m_ptr.owner_before(other.m_weak);
910 }
911}
912
913
914template<class T>
915struct std::hash<StormByte::Shared<T>> {
921 std::size_t operator()(const StormByte::Shared<T>& value) const noexcept {
922 return std::hash<T*>{}(value.get());
923 }
924};
925
926template<class T>
927struct std::hash<StormByte::Unique<T>> {
933 std::size_t operator()(const StormByte::Unique<T>& value) const noexcept {
934 return std::hash<T*>{}(value.get());
935 }
936};
937
Shared owner of a T allocated on Base's heap.
Definition safe_pointers.hxx:201
Shared(const Shared &) noexcept=default
Copy constructor.
friend Shared< X > ConstPointerCast(const Shared< Y > &) noexcept
long use_count() const noexcept
Number of Shared sharing this object.
Definition safe_pointers.hxx:312
static Shared< T > MakePointer(Args &&... args)
Allocate Target on Base's heap and own it as T.
Definition safe_pointers.hxx:360
friend Shared< X > ReinterpretPointerCast(const Shared< Y > &) noexcept
friend Shared< X > DynamicPointerCast(const Shared< Y > &) noexcept
bool owner_before(const Shared< U > &other) const noexcept
Ownership order, same as std::shared_ptr::owner_before.
Definition safe_pointers.hxx:338
friend bool operator!=(const Shared &lhs, std::nullptr_t) noexcept
Inequality with null.
Definition safe_pointers.hxx:378
T & operator*() const
Dereference.
Definition safe_pointers.hxx:280
friend bool operator==(const Shared &lhs, const Shared &rhs) noexcept
Same stored pointer.
Definition safe_pointers.hxx:406
Shared() noexcept=default
Empty owner.
friend Shared< X > StaticPointerCast(const Shared< Y > &) noexcept
T * get() const noexcept
Raw pointer, or null.
Definition safe_pointers.hxx:272
friend auto operator<=>(const Shared &lhs, const Shared &rhs) noexcept
Order of the stored pointers.
Definition safe_pointers.hxx:416
friend bool operator==(std::nullptr_t, const Shared &rhs) noexcept
Equality with null.
Definition safe_pointers.hxx:387
Shared(Shared &&) noexcept=default
Move constructor.
T element_type
Pointee type.
Definition safe_pointers.hxx:203
friend class Shared
Definition safe_pointers.hxx:450
friend bool operator!=(std::nullptr_t, const Shared &rhs) noexcept
Inequality with null.
Definition safe_pointers.hxx:396
Shared(const Shared< U > &other) noexcept
Share ownership with other.
Definition safe_pointers.hxx:223
void swap(Shared &other) noexcept
Exchange owners with other.
Definition safe_pointers.hxx:327
friend bool operator==(const Shared &lhs, std::nullptr_t) noexcept
Equality with null.
Definition safe_pointers.hxx:369
void reset() noexcept
Drop this owner's reference.
Definition safe_pointers.hxx:319
Shared(Shared< U > &&other) noexcept
Take ownership from other.
Definition safe_pointers.hxx:232
friend void swap(Shared &left, Shared &right) noexcept
Exchange left and right.
Definition safe_pointers.hxx:425
T * operator->() const noexcept
Member access.
Definition safe_pointers.hxx:288
Unique owner of a T allocated on Base's heap.
Definition safe_pointers.hxx:490
void swap(Unique &other) noexcept
Exchange owners with other.
Definition safe_pointers.hxx:609
friend auto operator<=>(const Unique &lhs, const Unique &rhs) noexcept
Order of the stored pointers.
Definition safe_pointers.hxx:679
static Unique< T > MakePointer(Args &&... args)
Allocate Target on Base's heap and own it as T.
Definition safe_pointers.hxx:623
friend bool operator==(std::nullptr_t, const Unique &rhs) noexcept
Equality with null.
Definition safe_pointers.hxx:650
T * operator->() const noexcept
Member access.
Definition safe_pointers.hxx:562
Unique(Unique< U > &&other) noexcept
Take ownership from other.
Definition safe_pointers.hxx:513
deleter_type & get_deleter() noexcept
Deleter stored in this owner.
Definition safe_pointers.hxx:586
friend void swap(Unique &left, Unique &right) noexcept
Exchange left and right.
Definition safe_pointers.hxx:688
Unique(const Unique &)=delete
Copy constructor.
const deleter_type & get_deleter() const noexcept
Deleter stored in this owner.
Definition safe_pointers.hxx:594
friend bool operator!=(std::nullptr_t, const Unique &rhs) noexcept
Inequality with null.
Definition safe_pointers.hxx:659
friend bool operator!=(const Unique &lhs, std::nullptr_t) noexcept
Inequality with null.
Definition safe_pointers.hxx:641
T * pointer
Stored pointer.
Definition safe_pointers.hxx:493
Unique() noexcept=default
Empty owner.
friend bool operator==(const Unique &lhs, std::nullptr_t) noexcept
Equality with null.
Definition safe_pointers.hxx:632
void reset() noexcept
Destroy the object and drop this owner.
Definition safe_pointers.hxx:601
T & operator*() const
Dereference.
Definition safe_pointers.hxx:554
Unique(Unique &&) noexcept=default
Move constructor.
T * get() const noexcept
Raw pointer, or null.
Definition safe_pointers.hxx:546
friend bool operator==(const Unique &lhs, const Unique &rhs) noexcept
Same stored pointer.
Definition safe_pointers.hxx:669
T element_type
Pointee type.
Definition safe_pointers.hxx:492
Non-owning observer of a Shared control block.
Definition safe_pointers.hxx:725
void reset() noexcept
Drop this observer.
Definition safe_pointers.hxx:802
Weak(Weak &&) noexcept=default
Move constructor.
Weak() noexcept=default
Empty observer.
friend void swap(Weak &left, Weak &right) noexcept
Exchange left and right.
Definition safe_pointers.hxx:841
long use_count() const noexcept
Number of Shared still holding the object.
Definition safe_pointers.hxx:795
T element_type
Pointee type.
Definition safe_pointers.hxx:727
bool owner_before(const Shared< U > &other) const noexcept
Ownership order against a Shared.
Definition safe_pointers.hxx:832
bool owner_before(const Weak< U > &other) const noexcept
Ownership order, same as std::weak_ptr::owner_before.
Definition safe_pointers.hxx:821
Weak(const Shared< U > &owner) noexcept
Observe owner.
Definition safe_pointers.hxx:746
bool expired() const noexcept
Whether the object is already gone.
Definition safe_pointers.hxx:787
void swap(Weak &other) noexcept
Exchange observers with other.
Definition safe_pointers.hxx:810
Weak(const Weak &) noexcept=default
Copy constructor.
Derived derives from Base (std::is_base_of).
Definition relations.hxx:113
Same type after stripping cv and references from both sides.
Definition relations.hxx:94
void Free(void *pointer) noexcept
Release a block obtained from Allocate.
Shared< T > MakeShared(Args &&... args)
Construct T on Base's heap and wrap it in Shared.
void * Allocate(std::size_t bytes)
Allocate bytes on Base's heap.
Unique< T > MakeUnique(Args &&... args)
Construct T on Base's heap and wrap it in Unique.
Root namespace of the StormByte suite.
Shared< T > StaticPointerCast(const Shared< U > &from) noexcept
static_cast of the stored pointer.
Definition safe_pointers.hxx:863
Shared< T > ConstPointerCast(const Shared< U > &from) noexcept
const_cast of the stored pointer.
Definition safe_pointers.hxx:887
Shared< T > ReinterpretPointerCast(const Shared< U > &from) noexcept
reinterpret_cast of the stored pointer.
Definition safe_pointers.hxx:899
Shared< T > DynamicPointerCast(const Shared< U > &from) noexcept
dynamic_cast of the stored pointer.
Definition safe_pointers.hxx:875
STL namespace.
std::shared_ptr allocator that uses Allocate / Free.
Definition safe_pointers.hxx:120
constexpr bool operator==(const Allocator< U > &) const noexcept
All Allocator instances compare equal.
Definition safe_pointers.hxx:158
void deallocate(T *pointer, std::size_t) noexcept
Release storage obtained from allocate.
Definition safe_pointers.hxx:148
T * allocate(std::size_t count)
Allocate count objects of T.
Definition safe_pointers.hxx:140
constexpr Allocator() noexcept=default
Default constructor.
T value_type
Allocator value type.
Definition safe_pointers.hxx:121
Destroy T and return its block to Free.
Definition safe_pointers.hxx:101
void operator()(U *pointer) const noexcept
Destroy pointer and free its block on Base's heap.
Definition safe_pointers.hxx:108
std::size_t operator()(const StormByte::Shared< T > &value) const noexcept
Hashes the stored address.
Definition safe_pointers.hxx:921
std::size_t operator()(const StormByte::Unique< T > &value) const noexcept
Hashes the stored address.
Definition safe_pointers.hxx:933
#define STORMBYTE_PUBLIC
Definition visibility.h:52