TheAtlasEngine
 
Loading...
Searching...
No Matches
memory.hpp
1/*
2 Copied from
3 https://github.com/libhal/libhal/blob/main/include/libhal/pointers.hpp
4
5 [atlas] modifications
6 1.) File renamed to memory
7 2.) In the atlas:: namespace.
8*/
9#include <atomic>
10#include <memory_resource>
11#include <type_traits>
12#include <utility>
13#include <array>
14#include <atomic>
15#include <optional>
16#include <cstdint>
17
18namespace atlas::memory {
19 // Forward declarations
20 template<typename T>
21 class strong_ptr;
22
23 template<typename T>
24 class weak_ptr;
25
26 namespace detail {
35 struct ref_info {
47 using destroy_fn_t = size_t(void const*);
48
50 std::pmr::polymorphic_allocator<> allocator;
51 destroy_fn_t* destroy;
52 std::atomic<int32_t> strong_count = 1;
53 std::atomic<int32_t> weak_count = 0;
54 };
55
61 inline void ptr_add_ref(ref_info* p_info) {
62 p_info->strong_count.fetch_add(1, std::memory_order_relaxed);
63 }
64
74 inline void ptr_release(ref_info* p_info) {
75 if (p_info->strong_count.fetch_sub(1, std::memory_order_acq_rel) ==
76 1) {
77 // No more strong references, destroy the object but keep
78 // control block if there are weak references
79
80 // Call the destroy function which will:
81 // 1. Call the destructor of the object
82 // 2. Return the size of the rc for deallocation when needed
83 size_t const object_size = p_info->destroy(p_info);
84
85 // If there are no weak references, deallocate memory
86 if (p_info->weak_count.load(std::memory_order_acquire) == 0) {
87 // Save allocator for deallocating
88 auto alloc = p_info->allocator;
89
90 // Deallocate memory
91 alloc.deallocate_bytes(p_info, object_size);
92 }
93 }
94 }
95
101 inline void ptr_add_weak(ref_info* p_info) {
102 p_info->weak_count.fetch_add(1, std::memory_order_relaxed);
103 }
104
113 inline void ptr_release_weak(ref_info* p_info) {
114 if (p_info->weak_count.fetch_sub(1, std::memory_order_acq_rel) ==
115 0) {
116 // No more weak references, check if we can deallocate
117 if (p_info->strong_count.load(std::memory_order_acquire) == 0) {
118 // No strong references either, get the size from the
119 // destroy function
120 auto const object_size = p_info->destroy(nullptr);
121
122 // Save allocator for deallocating
123 auto alloc = p_info->allocator;
124
125 // Deallocate memory
126 alloc.deallocate_bytes(p_info, object_size);
127 }
128 }
129 }
130
140 template<typename T>
141 struct rc {
142 ref_info m_info;
143 T m_object;
144
145 // Constructor that forwards arguments to the object
146 template<typename... Args>
147 rc(std::pmr::polymorphic_allocator<> p_alloc, Args&&... args)
148 : m_info{ .allocator = p_alloc, .destroy = &destroy_function }
149 , m_object(std::forward<Args>(args)...) {}
150
151 // Static function to destroy an instance and return its size
152 static size_t destroy_function(void const* p_object) {
153 if (p_object != nullptr) {
154 auto const* obj = static_cast<rc<T> const*>(p_object);
155 // Call destructor for the object only
156 obj->m_object.~T();
157 }
158 // Return size for future deallocation
159 return sizeof(rc<T>);
160 }
161 };
162 // Check if a type is an array or std::array
163 template<typename T>
164 struct is_array_like : std::false_type {};
165
166 // NOLINTBEGIN(modernize-avoid-c-arrays)
167 // Specialization for C-style arrays
168 template<typename T, std::size_t N>
169 struct is_array_like<T[N]> : std::true_type {};
170 // NOLINTEND(modernize-avoid-c-arrays)
171
172 // Specialization for std::array
173 template<typename T, std::size_t N>
174 struct is_array_like<std::array<T, N>> : std::true_type {};
175
176 // Helper variable template
177 template<typename T>
178 inline constexpr bool is_array_like_v = is_array_like<T>::value;
179
180 // Concept for array-like types
181 template<typename T>
182 concept array_like = is_array_like_v<T>;
183
184 // Concept for non-array-like types
185 template<typename T>
187 } // namespace detail
188
222 template<typename T>
224 public:
225 using element_type = T;
226
228 strong_ptr() = delete;
229
231 strong_ptr(std::nullptr_t) = delete;
232
240 strong_ptr(strong_ptr const& p_other) noexcept
241 : m_ctrl(p_other.m_ctrl)
242 , m_ptr(p_other.m_ptr) {
243 ptr_add_ref(m_ctrl);
244 }
245
255 template<typename U>
256 strong_ptr(strong_ptr<U> const& p_other) noexcept
257 requires(std::is_convertible_v<U*, T*>)
258 : m_ctrl(p_other.m_ctrl)
259 , m_ptr(p_other.m_ptr) {
260 ptr_add_ref(m_ctrl);
261 }
262
280 strong_ptr(strong_ptr&& p_other) noexcept
281 : m_ctrl(p_other.m_ctrl)
282 , m_ptr(p_other.m_ptr) {
283 ptr_add_ref(m_ctrl);
284 }
285
304 strong_ptr& operator=(strong_ptr&& p_other) noexcept {
305 if (this != &p_other) {
306 release();
307 m_ctrl = p_other.m_ctrl;
308 m_ptr = p_other.m_ptr;
309 ptr_add_ref(m_ctrl);
310 }
311 return *this;
312 }
313
327 template<typename U>
328 strong_ptr(strong_ptr<U> const&, void const*) noexcept {
329 // NOTE: The conditional used here is to prevent the compiler from
330 // jumping-the-gun and emitting the static assert error during
331 // template instantiation of the class. With this conditional, the
332 // error only appears when this constructor is used.
333 static_assert(
334 std::is_same_v<U, void> && !std::is_same_v<U, void>,
335 "Aliasing constructor only works with pointers-to-members "
336 "and does not work with arbitrary pointers like std::shared_ptr "
337 "allows.");
338 }
339
372 template<typename U, detail::non_array_like M>
373 strong_ptr(strong_ptr<U> const& p_other,
374 // clang-format off
375 M U::* p_member_ptr
376 // clang-format on
377 ) noexcept
378
379 : m_ctrl(p_other.m_ctrl)
380 , m_ptr(&((*p_other).*p_member_ptr)) {
381 ptr_add_ref(m_ctrl);
382 }
383
415 template<typename U, typename E, std::size_t N>
416 strong_ptr(strong_ptr<U> const& p_other,
417 // clang-format off
418 std::array<E, N> U::* p_array_ptr,
419 // clang-format on
420 std::size_t p_index) {
421 static_assert(std::is_convertible_v<E*, T*>,
422 "Array element type must be convertible to T");
423 throw_if_out_of_bounds(N, p_index);
424 m_ctrl = p_other.m_ctrl;
425 m_ptr = &((*p_other).*p_array_ptr)[p_index];
426 ptr_add_ref(m_ctrl);
427 }
428
429 // NOLINTBEGIN(modernize-avoid-c-arrays)
461 template<typename U, typename E, std::size_t N>
462 strong_ptr(strong_ptr<U> const& p_other,
463 E (U::*p_array_ptr)[N],
464 std::size_t p_index) {
465 static_assert(std::is_convertible_v<E*, T*>,
466 "Array element type must be convertible to T");
467 throw_if_out_of_bounds(N, p_index);
468 m_ctrl = p_other.m_ctrl;
469 m_ptr = &((*p_other).*p_array_ptr)[p_index];
470 ptr_add_ref(m_ctrl);
471 }
472 // NOLINTEND(modernize-avoid-c-arrays)
473
480 ~strong_ptr() { release(); }
481
490 strong_ptr& operator=(strong_ptr const& p_other) noexcept {
491 if (this != &p_other) {
492 release();
493 m_ctrl = p_other.m_ctrl;
494 m_ptr = p_other.m_ptr;
495 ptr_add_ref(m_ctrl);
496 }
497 return *this;
498 }
499
510 template<typename U>
511 strong_ptr& operator=(strong_ptr<U> const& p_other) noexcept
512 requires(std::is_convertible_v<U*, T*>)
513 {
514 release();
515 m_ctrl = p_other.m_ctrl;
516 m_ptr = p_other.m_ptr;
517 ptr_add_ref(m_ctrl);
518 return *this;
519 }
520
526 void swap(strong_ptr& p_other) noexcept {
527 std::swap(m_ctrl, p_other.m_ctrl);
528 std::swap(m_ptr, p_other.m_ptr);
529 }
530
534 T& operator*() && = delete;
535
539 T* operator->() && = delete;
540
546 [[nodiscard]] T& operator*() const& noexcept { return *m_ptr; }
547
553 [[nodiscard]] T* operator->() const& noexcept { return m_ptr; }
554
562 [[nodiscard]] auto use_count() const noexcept {
563 return m_ctrl ? m_ctrl->strong_count.load(std::memory_order_relaxed)
564 : 0;
565 }
566
567 private:
568 template<class U, typename... Args>
569 friend strong_ptr<U> make_strong_ptr(std::pmr::polymorphic_allocator<>,
570 Args&&...);
571
572 template<typename U>
573 friend class strong_ptr;
574
575 template<typename U>
576 friend class weak_ptr;
577
578 template<typename U>
579 friend class optional_ptr;
580
581 inline void throw_if_out_of_bounds(size_t p_size, size_t p_index) {
582 if (p_index >= p_size) {
583 throw std::out_of_range(
584 this, { .m_index = p_index, .m_capacity = p_size });
585 }
586 }
587
588 // Internal constructor with control block and pointer - used by make()
589 // and aliasing
590 strong_ptr(detail::ref_info* p_ctrl, T* p_ptr) noexcept
591 : m_ctrl(p_ctrl)
592 , m_ptr(p_ptr) {}
593
594 void release() {
595 if (m_ctrl) {
596 ptr_release(m_ctrl);
597 }
598 }
599
600 detail::ref_info* m_ctrl = nullptr;
601 T* m_ptr = nullptr;
602 };
603
631 template<typename T>
633 public:
641 [[nodiscard]] strong_ptr<T> strong_from_this() {
642 auto locked = m_weak_this.lock();
643 if (!locked) {
644 throw std::bad_weak_ptr(&weak_from_this);
645 }
646 return locked.value();
647 }
648
656 [[nodiscard]] strong_ptr<T const> strong_from_this() const {
657 auto locked = m_weak_this.lock();
658 if (!locked) {
659 throw std::bad_weak_ptr(&weak_from_this);
660 }
661 // Cast the strong_ptr<T> to strong_ptr<T const>
662 return strong_ptr<T const>(locked.value());
663 }
664
670 [[nodiscard]] weak_ptr<T> weak_from_this() noexcept {
671 return m_weak_this;
672 }
673
679 [[nodiscard]] weak_ptr<T const> weak_from_this() const noexcept {
680 return weak_ptr<T const>(m_weak_this);
681 }
682
683 protected:
688
696 // Intentionally don't copy m_weak_this
697 }
698
706 enable_strong_from_this const&) noexcept {
707 // Intentionally don't assign m_weak_this
708 return *this;
709 }
710
715
716 private:
717 template<class U, typename... Args>
718 friend strong_ptr<U> make_strong_ptr(std::pmr::polymorphic_allocator<>,
719 Args&&...);
720
726 void init_weak_this(strong_ptr<T> const& p_self) noexcept {
727 m_weak_this = p_self;
728 }
729
730 mutable weak_ptr<T> m_weak_this;
731 };
732
733 template<typename T>
734 class optional_ptr;
735
766 template<typename T>
767 class weak_ptr {
768 public:
769 template<typename U>
770 friend class strong_ptr;
771
772 template<typename U>
773 friend class weak_ptr;
774
775 using element_type = T;
776
780 weak_ptr() noexcept = default;
781
787 weak_ptr(strong_ptr<T> const& p_strong) noexcept
788 : m_ctrl(p_strong.m_ctrl)
789 , m_ptr(p_strong.m_ptr) {
790 if (m_ctrl) {
791 ptr_add_weak(m_ctrl);
792 }
793 }
794
800 weak_ptr(weak_ptr const& p_other) noexcept
801 : m_ctrl(p_other.m_ctrl)
802 , m_ptr(p_other.m_ptr) {
803 if (m_ctrl) {
804 ptr_add_weak(m_ctrl);
805 }
806 }
807
813 weak_ptr(weak_ptr&& p_other) noexcept
814 : m_ctrl(p_other.m_ctrl)
815 , m_ptr(p_other.m_ptr) {
816 p_other.m_ctrl = nullptr;
817 p_other.m_ptr = nullptr;
818 }
819
829 template<typename U>
830 weak_ptr(weak_ptr<U> const& p_other) noexcept
831 requires(std::is_convertible_v<U*, T*>)
832 : m_ctrl(p_other.m_ctrl)
833 , m_ptr(static_cast<T*>(p_other.m_ptr)) {
834 if (m_ctrl) {
835 ptr_add_weak(m_ctrl);
836 }
837 }
838
847 template<typename U>
848 weak_ptr(weak_ptr<U>&& p_other) noexcept
849 requires(std::is_convertible_v<U*, T*>)
850 : m_ctrl(p_other.m_ctrl)
851 , m_ptr(static_cast<T*>(p_other.m_ptr)) {
852 p_other.m_ctrl = nullptr;
853 p_other.m_ptr = nullptr;
854 }
855
864 template<typename U>
865 weak_ptr(strong_ptr<U> const& p_other) noexcept
866 requires(std::is_convertible_v<U*, T*>)
867 : m_ctrl(p_other.m_ctrl)
868 , m_ptr(static_cast<T*>(p_other.m_ptr)) {
869 if (m_ctrl) {
870 ptr_add_weak(m_ctrl);
871 }
872 }
873
881 if (m_ctrl) {
882 ptr_release_weak(m_ctrl);
883 }
884 }
885
892 weak_ptr& operator=(weak_ptr const& p_other) noexcept {
893 weak_ptr(p_other).swap(*this);
894 return *this;
895 }
896
903 weak_ptr& operator=(weak_ptr&& p_other) noexcept {
904 weak_ptr(std::move(p_other)).swap(*this);
905 return *this;
906 }
907
914 weak_ptr& operator=(strong_ptr<T> const& p_strong) noexcept {
915 weak_ptr(p_strong).swap(*this);
916 return *this;
917 }
918
924 void swap(weak_ptr& p_other) noexcept {
925 std::swap(m_ctrl, p_other.m_ctrl);
926 std::swap(m_ptr, p_other.m_ptr);
927 }
928
934 [[nodiscard]] bool expired() const noexcept {
935 return !m_ctrl ||
936 m_ctrl->strong_count.load(std::memory_order_relaxed) == 0;
937 }
938
947 [[nodiscard]] optional_ptr<T> lock() const noexcept;
948
956 [[nodiscard]] auto use_count() const noexcept {
957 return m_ctrl ? m_ctrl->strong_count.load(std::memory_order_relaxed)
958 : 0;
959 }
960
961 private:
962 detail::ref_info* m_ctrl = nullptr;
963 T* m_ptr = nullptr;
964 };
965
1001 template<typename T>
1003 public:
1007 constexpr optional_ptr() noexcept {}
1008
1012 constexpr optional_ptr(std::nullptr_t) noexcept {}
1013
1017 constexpr optional_ptr(optional_ptr&& p_other) noexcept = delete;
1018
1024 constexpr optional_ptr(strong_ptr<T> const& value) noexcept
1025 : m_value(value) {}
1026
1032 constexpr optional_ptr(optional_ptr const& p_other) { *this = p_other; }
1033
1040 template<typename U>
1041 constexpr optional_ptr(strong_ptr<U> const& p_value)
1042 requires(std::is_convertible_v<U*, T*>)
1043 {
1044 *this = p_value;
1045 }
1046
1050 constexpr optional_ptr& operator=(optional_ptr&& other) noexcept =
1051 delete;
1052
1059 constexpr optional_ptr& operator=(optional_ptr const& other) {
1060 if (this != &other) {
1061 if (is_engaged() && other.is_engaged()) {
1062 m_value = other.m_value;
1063 }
1064 else if (is_engaged() && not other.is_engaged()) {
1065 reset();
1066 }
1067 else if (not is_engaged() && other.is_engaged()) {
1068 new (&m_value) strong_ptr<T>(other.m_value);
1069 }
1070 }
1071 return *this;
1072 }
1073
1080 constexpr optional_ptr& operator=(strong_ptr<T> const& value) noexcept {
1081 if (is_engaged()) {
1082 m_value = value;
1083 }
1084 else {
1085 new (&m_value) strong_ptr<T>(value);
1086 }
1087 return *this;
1088 }
1089
1097 template<typename U>
1098 constexpr optional_ptr& operator=(strong_ptr<U> const& p_value) noexcept
1099 requires(std::is_convertible_v<U*, T*>)
1100 {
1101 if (is_engaged()) {
1102 m_value = p_value;
1103 }
1104 else {
1105 new (&m_value) strong_ptr<U>(p_value);
1106 }
1107 return *this;
1108 }
1109
1115 constexpr optional_ptr& operator=(std::nullptr_t) noexcept {
1116 reset();
1117 return *this;
1118 }
1119
1126 if (is_engaged()) {
1127 m_value.~strong_ptr<T>();
1128 }
1129 }
1130
1136 [[nodiscard]] constexpr bool has_value() const noexcept {
1137 return is_engaged();
1138 }
1139
1145 constexpr explicit operator bool() const noexcept {
1146 return is_engaged();
1147 }
1148
1155 [[nodiscard]] constexpr strong_ptr<T>& value() {
1156 if (!is_engaged()) {
1157 throw std::bad_optional_access();
1158 }
1159 return m_value;
1160 }
1161
1169 [[nodiscard]] constexpr strong_ptr<T> const& value() const {
1170 if (!is_engaged()) {
1171 throw std::bad_optional_access();
1172 }
1173 return m_value;
1174 }
1175
1185 [[nodiscard]] constexpr operator strong_ptr<T>() { return value(); }
1186
1193 [[nodiscard]] constexpr operator strong_ptr<T>() const {
1194 return value();
1195 }
1196
1208 template<typename U>
1209 [[nodiscard]] constexpr operator strong_ptr<U>()
1210 requires(std::is_convertible_v<T*, U*> && !std::is_same_v<T, U>)
1211 {
1212 if (!is_engaged()) {
1213 throw std::bad_optional_access();
1214 }
1215 // strong_ptr handles the polymorphic conversion
1216 return strong_ptr<U>(m_value);
1217 }
1218
1228 template<typename U>
1229 [[nodiscard]] constexpr operator strong_ptr<U>() const
1230 requires(std::is_convertible_v<T*, U*> && !std::is_same_v<T, U>)
1231 {
1232 if (!is_engaged()) {
1233 // hal::safe_throw(hal::bad_optional_ptr_access(this));
1234 throw std::bad_optional_access();
1235 }
1236 // strong_ptr handles the polymorphic conversion
1237 return strong_ptr<U>(m_value);
1238 }
1239
1245 [[nodiscard]] constexpr auto* operator->() {
1246 auto& ref = *(this->value());
1247 return &ref;
1248 }
1249
1256 [[nodiscard]] constexpr auto* operator->() const {
1257 auto& ref = *(this->value());
1258 return &ref;
1259 }
1260
1266 [[nodiscard]] constexpr auto& operator*() {
1267 auto& ref = *(this->value());
1268 return ref;
1269 }
1270
1277 [[nodiscard]] constexpr auto& operator*() const {
1278 auto& ref = *(this->value());
1279 return ref;
1280 }
1281
1285 constexpr void reset() noexcept {
1286 if (is_engaged()) {
1287 m_value.~strong_ptr<T>();
1288 m_raw_ptrs[0] = nullptr;
1289 m_raw_ptrs[1] = nullptr;
1290 }
1291 }
1292
1302 template<typename... Args>
1303 constexpr strong_ptr<T>& emplace(Args&&... args) {
1304 reset();
1305 new (&m_value) strong_ptr<T>(std::forward<Args>(args)...);
1306 return m_value;
1307 }
1308
1314 constexpr void swap(optional_ptr& other) noexcept {
1315 if (is_engaged() && other.is_engaged()) {
1316 std::swap(m_value, other.m_value);
1317 }
1318 else if (is_engaged() && !other.is_engaged()) {
1319 new (&other.m_value) strong_ptr<T>(std::move(m_value));
1320 reset();
1321 }
1322 else if (!is_engaged() && other.is_engaged()) {
1323 new (&m_value) strong_ptr<T>(std::move(other.m_value));
1324 other.reset();
1325 }
1326 }
1327
1328 private:
1335 union {
1336 std::array<void*, 2> m_raw_ptrs = { nullptr, nullptr };
1337 strong_ptr<T> m_value;
1338 };
1339
1340 // Ensure the strong_ptr layout matches our expectations
1341 static_assert(sizeof(m_value) == sizeof(m_raw_ptrs),
1342 "strong_ptr must be exactly the size of two pointers");
1343
1349 [[nodiscard]] constexpr bool is_engaged() const noexcept {
1350 return m_raw_ptrs[0] != nullptr || m_raw_ptrs[1] != nullptr;
1351 }
1352 };
1353
1365 template<typename T>
1366 [[nodiscard]] inline optional_ptr<T> weak_ptr<T>::lock() const noexcept {
1367 if (expired()) {
1368 return nullptr;
1369 }
1370
1371 // Try to increment the strong count
1372 auto current_count =
1373 m_ctrl->strong_count.load(std::memory_order_relaxed);
1374 while (current_count > 0) {
1375 // TODO(kammce): Consider if this is dangerous
1376 if (m_ctrl->strong_count.compare_exchange_weak(
1377 current_count,
1378 current_count + 1,
1379 std::memory_order_acq_rel,
1380 std::memory_order_relaxed)) {
1381 // Successfully incremented
1382 auto obj = strong_ptr<T>(m_ctrl, m_ptr);
1383 return obj;
1384 }
1385 }
1386
1387 // Strong count is now 0
1388 return nullptr;
1389 }
1390
1398 template<typename T>
1399 void swap(strong_ptr<T>& p_lhs, strong_ptr<T>& p_rhs) noexcept {
1400 p_lhs.swap(p_rhs);
1401 }
1402
1410 template<typename T>
1411 void swap(weak_ptr<T>& p_lhs, weak_ptr<T>& p_rhs) noexcept {
1412 p_lhs.swap(p_rhs);
1413 }
1414
1426 template<typename T, typename U>
1427 bool operator==(strong_ptr<T> const& p_lhs,
1428 strong_ptr<U> const& p_rhs) noexcept {
1429 return p_lhs.operator->() == p_rhs.operator->();
1430 }
1431
1443 template<typename T, typename U>
1444 bool operator!=(strong_ptr<T> const& p_lhs,
1445 strong_ptr<U> const& p_rhs) noexcept {
1446 return !(p_lhs == p_rhs);
1447 }
1461 template<typename T, typename U>
1462 [[nodiscard]] bool operator==(optional_ptr<T> const& p_lhs,
1463 optional_ptr<U> const& p_rhs) noexcept {
1464 // If both are disengaged, they're equal
1465 if (!p_lhs.has_value() && !p_rhs.has_value()) {
1466 return true;
1467 }
1468
1469 // If one is engaged and the other isn't, they're not equal
1470 if (p_lhs.has_value() != p_rhs.has_value()) {
1471 return false;
1472 }
1473
1474 // Both are engaged, compare the underlying pointers
1475 return p_lhs.value().operator->() == p_rhs.value().operator->();
1476 }
1477
1489 template<typename T, typename U>
1490 [[nodiscard]] bool operator!=(optional_ptr<T> const& p_lhs,
1491 optional_ptr<U> const& p_rhs) noexcept {
1492 return !(p_lhs == p_rhs);
1493 }
1494
1504 template<typename T>
1505 [[nodiscard]] bool operator==(optional_ptr<T> const& p_lhs,
1506 std::nullptr_t) noexcept {
1507 return !p_lhs.has_value();
1508 }
1509
1519 template<typename T>
1520 [[nodiscard]] bool operator==(std::nullptr_t,
1521 optional_ptr<T> const& p_rhs) noexcept {
1522 return !p_rhs.has_value();
1523 }
1524
1534 template<typename T>
1535 [[nodiscard]] bool operator!=(optional_ptr<T> const& p_lhs,
1536 std::nullptr_t) noexcept {
1537 return p_lhs.has_value();
1538 }
1539
1549 template<typename T>
1550 [[nodiscard]] bool operator!=(std::nullptr_t,
1551 optional_ptr<T> const& p_rhs) noexcept {
1552 return p_rhs.has_value();
1553 }
1554
1562 private:
1563 strong_ptr_only_token() = default;
1564
1565 template<class U, typename... Args>
1566 friend strong_ptr<U> make_strong_ptr(std::pmr::polymorphic_allocator<>,
1567 Args&&...);
1568 };
1569
1636 template<class T, typename... Args>
1637 [[nodiscard]] inline strong_ptr<T> make_strong_ptr(
1638 std::pmr::polymorphic_allocator<> p_alloc,
1639 Args&&... p_args) {
1640 using rc_t = detail::rc<T>;
1641
1642 rc_t* obj = nullptr;
1643
1644 if constexpr (std::
1645 is_constructible_v<T, strong_ptr_only_token, Args...>) {
1646 // Type expects token as first parameter
1647 obj = p_alloc.new_object<rc_t>(
1648 p_alloc, strong_ptr_only_token{}, std::forward<Args>(p_args)...);
1649 }
1650 else {
1651 // Normal type, construct without token
1652 obj =
1653 p_alloc.new_object<rc_t>(p_alloc, std::forward<Args>(p_args)...);
1654 }
1655
1656 strong_ptr<T> result(&obj->m_info, &obj->m_object);
1657
1658 // Initialize enable_strong_from_this if the type inherits from it
1659 if constexpr (std::is_base_of_v<enable_strong_from_this<T>, T>) {
1660 result->init_weak_this(result);
1661 }
1662
1663 return result;
1664 }
1665} // namespace atlas::memory
CRTP mixin to enable objects to create strong_ptr instances to themselves.
Definition memory.hpp:632
~enable_strong_from_this()=default
Protected destructor.
strong_ptr< T const > strong_from_this() const
Get a strong_ptr to this object (const version)
Definition memory.hpp:656
enable_strong_from_this & operator=(enable_strong_from_this const &) noexcept
Protected assignment operator.
Definition memory.hpp:705
weak_ptr< T > weak_from_this() noexcept
Get a weak_ptr to this object.
Definition memory.hpp:670
strong_ptr< T > strong_from_this()
Get a strong_ptr to this object.
Definition memory.hpp:641
enable_strong_from_this(enable_strong_from_this const &) noexcept
Protected copy constructor.
Definition memory.hpp:695
enable_strong_from_this()=default
Protected constructor to prevent direct instantiation.
weak_ptr< T const > weak_from_this() const noexcept
Get a weak_ptr to this object (const version)
Definition memory.hpp:679
Optional, nullable, smart pointer that works with hal::strong_ptr.
Definition memory.hpp:1002
constexpr auto * operator->()
Arrow operator for accessing members of the contained object.
Definition memory.hpp:1245
constexpr strong_ptr< T > & emplace(Args &&... args)
Emplace a new value.
Definition memory.hpp:1303
constexpr auto * operator->() const
Arrow operator for accessing members of the contained object (const version)
Definition memory.hpp:1256
constexpr optional_ptr & operator=(strong_ptr< U > const &p_value) noexcept
Converting assignment from a strong_ptr.
Definition memory.hpp:1098
constexpr void swap(optional_ptr &other) noexcept
Swap the contents of this optional_ptr with another.
Definition memory.hpp:1314
constexpr optional_ptr(strong_ptr< U > const &p_value)
Converting constructor from a strong_ptr.
Definition memory.hpp:1041
constexpr auto & operator*()
Dereference operator for accessing the contained object.
Definition memory.hpp:1266
constexpr optional_ptr & operator=(std::nullptr_t) noexcept
Assignment from nullptr (resets to disengaged state)
Definition memory.hpp:1115
constexpr strong_ptr< T > & value()
Access the contained value, throw if not engaged.
Definition memory.hpp:1155
constexpr optional_ptr(std::nullptr_t) noexcept
Constructor for nullptr (creates a disengaged optional)
Definition memory.hpp:1012
constexpr optional_ptr(optional_ptr const &p_other)
Copy constructor.
Definition memory.hpp:1032
constexpr void reset() noexcept
Reset the optional to a disengaged state.
Definition memory.hpp:1285
constexpr bool has_value() const noexcept
Check if the optional_ptr is engaged.
Definition memory.hpp:1136
constexpr optional_ptr(optional_ptr &&p_other) noexcept=delete
Move constructor is deleted.
constexpr optional_ptr(strong_ptr< T > const &value) noexcept
Construct from a strong_ptr lvalue.
Definition memory.hpp:1024
~optional_ptr()
Destructor.
Definition memory.hpp:1125
constexpr optional_ptr & operator=(optional_ptr const &other)
Copy assignment operator.
Definition memory.hpp:1059
constexpr optional_ptr & operator=(strong_ptr< T > const &value) noexcept
Assignment from a strong_ptr.
Definition memory.hpp:1080
constexpr auto & operator*() const
Dereference operator for accessing the contained object (const version)
Definition memory.hpp:1277
constexpr optional_ptr() noexcept
Default constructor creates a disengaged optional.
Definition memory.hpp:1007
constexpr strong_ptr< T > const & value() const
Access the contained value, throw if not engaged (const version)
Definition memory.hpp:1169
constexpr optional_ptr & operator=(optional_ptr &&other) noexcept=delete
Move assignment operator is deleted.
A construction token that can only be created by make_strong_ptr.
Definition memory.hpp:1561
A non-nullable strong reference counted pointer.
Definition memory.hpp:223
strong_ptr(strong_ptr< U > const &p_other) noexcept
Converting copy constructor.
Definition memory.hpp:256
strong_ptr(strong_ptr const &p_other) noexcept
Copy constructor.
Definition memory.hpp:240
strong_ptr(std::nullptr_t)=delete
Delete nullptr constructor - strong_ptr must always be valid.
strong_ptr(strong_ptr< U > const &p_other, E(U::*p_array_ptr)[N], std::size_t p_index)
Safe aliasing constructor for C-array members.
Definition memory.hpp:462
T & operator*() const &noexcept
Dereference operator to access the managed object.
Definition memory.hpp:546
strong_ptr()=delete
Delete default constructor - strong_ptr must always be valid.
T * operator->() const &noexcept
Member access operator to access the managed object.
Definition memory.hpp:553
strong_ptr(strong_ptr< U > const &p_other, M U::*p_member_ptr) noexcept
Safe aliasing constructor for object members.
Definition memory.hpp:373
strong_ptr & operator=(strong_ptr< U > const &p_other) noexcept
Converting copy assignment operator.
Definition memory.hpp:511
strong_ptr & operator=(strong_ptr &&p_other) noexcept
Move assignment operator that behaves like a copy assignment for safety.
Definition memory.hpp:304
strong_ptr(strong_ptr &&p_other) noexcept
Move constructor that intentionally behaves like a copy constructor for safety.
Definition memory.hpp:280
~strong_ptr()
Destructor.
Definition memory.hpp:480
auto use_count() const noexcept
Get the current reference count.
Definition memory.hpp:562
strong_ptr & operator=(strong_ptr const &p_other) noexcept
Copy assignment operator.
Definition memory.hpp:490
T & operator*() &&=delete
Disable dereferencing for r-values (temporaries)
T * operator->() &&=delete
Disable member access for r-values (temporaries)
void swap(strong_ptr &p_other) noexcept
Swap the contents of this strong_ptr with another.
Definition memory.hpp:526
strong_ptr(strong_ptr< U > const &, void const *) noexcept
Compile time error message for bad alias value.
Definition memory.hpp:328
strong_ptr(strong_ptr< U > const &p_other, std::array< E, N > U::*p_array_ptr, std::size_t p_index)
Safe aliasing constructor for std::array members.
Definition memory.hpp:416
A weak reference to a strong_ptr.
Definition memory.hpp:767
weak_ptr & operator=(strong_ptr< T > const &p_strong) noexcept
Assignment from strong_ptr.
Definition memory.hpp:914
weak_ptr(weak_ptr const &p_other) noexcept
Copy constructor.
Definition memory.hpp:800
weak_ptr(strong_ptr< U > const &p_other) noexcept
Converting constructor from strong_ptr.
Definition memory.hpp:865
weak_ptr(weak_ptr< U > &&p_other) noexcept
Converting move constructor.
Definition memory.hpp:848
~weak_ptr()
Destructor.
Definition memory.hpp:880
optional_ptr< T > lock() const noexcept
Attempt to obtain a strong_ptr to the referenced object.
Definition memory.hpp:1366
weak_ptr(weak_ptr< U > const &p_other) noexcept
Converting copy constructor.
Definition memory.hpp:830
bool expired() const noexcept
Check if the referenced object has been destroyed.
Definition memory.hpp:934
weak_ptr(weak_ptr &&p_other) noexcept
Move constructor.
Definition memory.hpp:813
void swap(weak_ptr &p_other) noexcept
Swap the contents of this weak_ptr with another.
Definition memory.hpp:924
auto use_count() const noexcept
Get the current strong reference count.
Definition memory.hpp:956
weak_ptr() noexcept=default
Default constructor creates empty weak_ptr.
weak_ptr & operator=(weak_ptr const &p_other) noexcept
Copy assignment operator.
Definition memory.hpp:892
weak_ptr & operator=(weak_ptr &&p_other) noexcept
Move assignment operator.
Definition memory.hpp:903
Definition memory.hpp:182
Definition memory.hpp:186
Definition memory.hpp:164
A wrapper that contains both the ref_info and the actual object.
Definition memory.hpp:141
Control block for reference counting - type erased.
Definition memory.hpp:35
std::pmr::polymorphic_allocator allocator
Initialize to 1 since creation implies a reference.
Definition memory.hpp:50
size_t(void const *) destroy_fn_t
Destroy function for ref counted object.
Definition memory.hpp:47