10#include <memory_resource>
18namespace atlas::memory {
52 std::atomic<int32_t> strong_count = 1;
53 std::atomic<int32_t> weak_count = 0;
61 inline void ptr_add_ref(
ref_info* p_info) {
62 p_info->strong_count.fetch_add(1, std::memory_order_relaxed);
74 inline void ptr_release(ref_info* p_info) {
75 if (p_info->strong_count.fetch_sub(1, std::memory_order_acq_rel) ==
83 size_t const object_size = p_info->destroy(p_info);
86 if (p_info->weak_count.load(std::memory_order_acquire) == 0) {
88 auto alloc = p_info->allocator;
91 alloc.deallocate_bytes(p_info, object_size);
101 inline void ptr_add_weak(ref_info* p_info) {
102 p_info->weak_count.fetch_add(1, std::memory_order_relaxed);
113 inline void ptr_release_weak(ref_info* p_info) {
114 if (p_info->weak_count.fetch_sub(1, std::memory_order_acq_rel) ==
117 if (p_info->strong_count.load(std::memory_order_acquire) == 0) {
120 auto const object_size = p_info->destroy(
nullptr);
123 auto alloc = p_info->allocator;
126 alloc.deallocate_bytes(p_info, object_size);
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)...) {}
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);
159 return sizeof(rc<T>);
168 template<
typename T, std::
size_t N>
173 template<
typename T, std::
size_t N>
225 using element_type = T;
241 : m_ctrl(p_other.m_ctrl)
242 , m_ptr(p_other.m_ptr) {
257 requires(std::is_convertible_v<U*, T*>)
258 : m_ctrl(p_other.m_ctrl)
259 , m_ptr(p_other.m_ptr) {
281 : m_ctrl(p_other.m_ctrl)
282 , m_ptr(p_other.m_ptr) {
305 if (
this != &p_other) {
307 m_ctrl = p_other.m_ctrl;
308 m_ptr = p_other.m_ptr;
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 "
372 template<
typename U, detail::non_array_like M>
379 : m_ctrl(p_other.m_ctrl)
380 , m_ptr(&((*p_other).*p_member_ptr)) {
415 template<
typename U,
typename E, std::
size_t N>
418 std::array<E, N> U::* p_array_ptr,
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];
461 template<
typename U,
typename E, std::
size_t N>
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];
491 if (
this != &p_other) {
493 m_ctrl = p_other.m_ctrl;
494 m_ptr = p_other.m_ptr;
512 requires(std::is_convertible_v<U*, T*>)
515 m_ctrl = p_other.m_ctrl;
516 m_ptr = p_other.m_ptr;
527 std::swap(m_ctrl, p_other.m_ctrl);
528 std::swap(m_ptr, p_other.m_ptr);
546 [[nodiscard]] T&
operator*() const& noexcept {
return *m_ptr; }
553 [[nodiscard]] T*
operator->() const& noexcept {
return m_ptr; }
563 return m_ctrl ? m_ctrl->strong_count.load(std::memory_order_relaxed)
568 template<
class U,
typename... Args>
569 friend strong_ptr<U> make_strong_ptr(std::pmr::polymorphic_allocator<>,
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 });
590 strong_ptr(detail::ref_info* p_ctrl, T* p_ptr) noexcept
600 detail::ref_info* m_ctrl =
nullptr;
642 auto locked = m_weak_this.lock();
646 return locked.value();
657 auto locked = m_weak_this.lock();
662 return strong_ptr<T const>(locked.value());
680 return weak_ptr<T const>(m_weak_this);
717 template<
class U,
typename... Args>
718 friend strong_ptr<U> make_strong_ptr(std::pmr::polymorphic_allocator<>,
726 void init_weak_this(strong_ptr<T>
const& p_self)
noexcept {
727 m_weak_this = p_self;
730 mutable weak_ptr<T> m_weak_this;
775 using element_type = T;
788 : m_ctrl(p_strong.m_ctrl)
789 , m_ptr(p_strong.m_ptr) {
791 ptr_add_weak(m_ctrl);
801 : m_ctrl(p_other.m_ctrl)
802 , m_ptr(p_other.m_ptr) {
804 ptr_add_weak(m_ctrl);
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;
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)) {
835 ptr_add_weak(m_ctrl);
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;
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)) {
870 ptr_add_weak(m_ctrl);
882 ptr_release_weak(m_ctrl);
925 std::swap(m_ctrl, p_other.m_ctrl);
926 std::swap(m_ptr, p_other.m_ptr);
936 m_ctrl->strong_count.load(std::memory_order_relaxed) == 0;
947 [[nodiscard]] optional_ptr<T>
lock() const noexcept;
957 return m_ctrl ? m_ctrl->strong_count.load(std::memory_order_relaxed)
1001 template<
typename T>
1040 template<
typename U>
1042 requires(std::is_convertible_v<U*, T*>)
1060 if (
this != &other) {
1061 if (is_engaged() && other.is_engaged()) {
1062 m_value = other.m_value;
1064 else if (is_engaged() && not other.is_engaged()) {
1067 else if (not is_engaged() && other.is_engaged()) {
1068 new (&m_value) strong_ptr<T>(other.m_value);
1085 new (&m_value) strong_ptr<T>(
value);
1097 template<
typename U>
1099 requires(std::is_convertible_v<U*, T*>)
1105 new (&m_value) strong_ptr<U>(p_value);
1127 m_value.~strong_ptr<T>();
1137 return is_engaged();
1145 constexpr explicit operator bool() const noexcept {
1146 return is_engaged();
1155 [[nodiscard]]
constexpr strong_ptr<T>&
value() {
1156 if (!is_engaged()) {
1157 throw std::bad_optional_access();
1169 [[nodiscard]]
constexpr strong_ptr<T>
const&
value()
const {
1170 if (!is_engaged()) {
1171 throw std::bad_optional_access();
1185 [[nodiscard]]
constexpr operator strong_ptr<T>() {
return value(); }
1193 [[nodiscard]]
constexpr operator strong_ptr<T>()
const {
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>)
1212 if (!is_engaged()) {
1213 throw std::bad_optional_access();
1216 return strong_ptr<U>(m_value);
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>)
1232 if (!is_engaged()) {
1234 throw std::bad_optional_access();
1237 return strong_ptr<U>(m_value);
1246 auto& ref = *(this->
value());
1257 auto& ref = *(this->
value());
1267 auto& ref = *(this->
value());
1278 auto& ref = *(this->
value());
1287 m_value.~strong_ptr<T>();
1288 m_raw_ptrs[0] =
nullptr;
1289 m_raw_ptrs[1] =
nullptr;
1302 template<
typename... Args>
1303 constexpr strong_ptr<T>&
emplace(Args&&... args) {
1305 new (&m_value) strong_ptr<T>(std::forward<Args>(args)...);
1315 if (is_engaged() && other.is_engaged()) {
1316 std::swap(m_value, other.m_value);
1318 else if (is_engaged() && !other.is_engaged()) {
1319 new (&other.m_value) strong_ptr<T>(std::move(m_value));
1322 else if (!is_engaged() && other.is_engaged()) {
1323 new (&m_value) strong_ptr<T>(std::move(other.m_value));
1336 std::array<void*, 2> m_raw_ptrs = {
nullptr,
nullptr };
1337 strong_ptr<T> m_value;
1341 static_assert(
sizeof(m_value) ==
sizeof(m_raw_ptrs),
1342 "strong_ptr must be exactly the size of two pointers");
1349 [[nodiscard]]
constexpr bool is_engaged() const noexcept {
1350 return m_raw_ptrs[0] !=
nullptr || m_raw_ptrs[1] !=
nullptr;
1365 template<
typename T>
1372 auto current_count =
1373 m_ctrl->strong_count.load(std::memory_order_relaxed);
1374 while (current_count > 0) {
1376 if (m_ctrl->strong_count.compare_exchange_weak(
1379 std::memory_order_acq_rel,
1380 std::memory_order_relaxed)) {
1382 auto obj = strong_ptr<T>(m_ctrl, m_ptr);
1398 template<
typename T>
1399 void swap(strong_ptr<T>& p_lhs, strong_ptr<T>& p_rhs)
noexcept {
1410 template<
typename T>
1411 void swap(weak_ptr<T>& p_lhs, weak_ptr<T>& p_rhs)
noexcept {
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->();
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);
1461 template<
typename T,
typename U>
1462 [[nodiscard]]
bool operator==(optional_ptr<T>
const& p_lhs,
1463 optional_ptr<U>
const& p_rhs)
noexcept {
1465 if (!p_lhs.has_value() && !p_rhs.has_value()) {
1470 if (p_lhs.has_value() != p_rhs.has_value()) {
1475 return p_lhs.value().operator->() == p_rhs.value().operator->();
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);
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();
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();
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();
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();
1565 template<
class U,
typename... Args>
1566 friend strong_ptr<U> make_strong_ptr(std::pmr::polymorphic_allocator<>,
1636 template<
class T,
typename... Args>
1637 [[nodiscard]]
inline strong_ptr<T> make_strong_ptr(
1638 std::pmr::polymorphic_allocator<> p_alloc,
1642 rc_t* obj =
nullptr;
1647 obj = p_alloc.new_object<rc_t>(
1653 p_alloc.new_object<rc_t>(p_alloc, std::forward<Args>(p_args)...);
1656 strong_ptr<T> result(&obj->m_info, &obj->m_object);
1659 if constexpr (std::is_base_of_v<enable_strong_from_this<T>, T>) {
1660 result->init_weak_this(result);
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