aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/kstd/kstd/bits/shared_ptr.hpp286
1 files changed, 192 insertions, 94 deletions
diff --git a/libs/kstd/kstd/bits/shared_ptr.hpp b/libs/kstd/kstd/bits/shared_ptr.hpp
index 03c107c0..c82071a2 100644
--- a/libs/kstd/kstd/bits/shared_ptr.hpp
+++ b/libs/kstd/kstd/bits/shared_ptr.hpp
@@ -2,6 +2,7 @@
#define KSTD_BITS_SHARED_PTR_HPP
#include <atomic>
+#include <concepts>
#include <cstddef>
#include <type_traits>
#include <utility>
@@ -10,51 +11,85 @@
namespace kstd
{
- /**
- * @brief Control block for shared_ptr and weak_ptr. This control block contains the reference counts for shared
- * ownership and weak ownership. The shared_count tracks the number of shared_ptr instances that own the managed
- * object, while the weak_count tracks the number of weak_ptr instances that reference the managed object.
- * The weak_count is needed to determine when it is safe to delete the shared_control_block itself
- */
- struct shared_control_block
+
+ namespace bits
{
- std::atomic<std::size_t> shared_count;
- std::atomic<std::size_t> weak_count;
+ //! The control block used by shared pointers and weak pointers.
+ //!
+ //! @see shared_ptr
+ //! @see weak_ptr
+ struct shared_control_block
+ {
+ //! The number of strong references to the managed object.
+ std::atomic<std::size_t> strong_count;
+ //! The number of weak references to the managed object.
+ std::atomic<std::size_t> weak_count;
- explicit shared_control_block(std::size_t shared = 1, std::size_t weak = 1)
- : shared_count(shared)
- , weak_count(weak)
- {}
- };
+ //! Construct a new control block with a given number of references.
+ //!
+ //! @param shared The number of strong references.
+ //! @param weak The number of weak references.
+ explicit shared_control_block(std::size_t shared = 1, std::size_t weak = 1)
+ : strong_count(shared)
+ , weak_count(weak)
+ {}
+ };
+
+ template<typename Y, typename T>
+ struct is_shared_pointer_ctor_compatible : std::false_type
+ {
+ };
+
+ template<typename Y, typename T>
+ struct is_shared_pointer_ctor_compatible<Y *, T *> : std::is_convertible<Y *, T *>
+ {
+ };
+
+ template<typename U, std::size_t N>
+ struct is_shared_pointer_ctor_compatible<U (*)[N], U (*)[]> // NOLINT(modernize-avoid-c-arrays)
+ : std::true_type
+ {
+ };
+
+ template<typename U, std::size_t N>
+ struct is_shared_pointer_ctor_compatible<U (*)[N], U const (*)[]> // NOLINT(modernize-avoid-c-arrays)
+ : std::true_type
+ {
+ };
+
+ template<typename U, std::size_t N>
+ struct is_shared_pointer_ctor_compatible<U (*)[N], U volatile (*)[]> // NOLINT(modernize-avoid-c-arrays)
+ : std::true_type
+ {
+ };
+
+ template<typename U, std::size_t N>
+ struct is_shared_pointer_ctor_compatible<U (*)[N], U volatile const (*)[]> // NOLINT(modernize-avoid-c-arrays)
+ : std::true_type
+ {
+ };
+ } // namespace bits
template<typename T>
struct shared_ptr;
- /**
- * @brief weak_ptr is a smart pointer that holds a non-owning weak reference to an object that is managed by
- * shared_ptr. It must be converted to shared_ptr in to be able to access the referenced object. A weak_ptr is created
- * as a copy of a shared_ptr, or as a copy of another weak_ptr. A weak_ptr is typically used to break circular
- * references between shared_ptr to avoid memory leaks. A weak_ptr does not contribute to the reference count of the
- * object, and it does not manage the lifetime of the object. If the object managed by shared_ptr is destroyed, the
- * weak_ptr becomes expired and cannot be used to access the object anymore.
- */
+ //! A pointer type to hold a non-owning reference to an object managed by a share pointer.
+ //!
+ //! @tparam T The type of the object referenced by this weak pointer.
template<typename T>
struct weak_ptr
{
template<typename U>
friend struct shared_ptr;
- /**
- * @brief Constructs a null weak_ptr.
- */
- weak_ptr() noexcept
+ //! Construct an empty weak pointer.
+ constexpr weak_ptr() noexcept
: m_storage(nullptr)
, m_control_block(nullptr)
{}
- template<typename U>
- requires(std::is_convertible_v<U *, T *>)
- weak_ptr(shared_ptr<U> const & other)
+ //! Construct a weak pointer which shares ownership of the object managed by other.
+ constexpr weak_ptr(weak_ptr const & other) noexcept
: m_storage(other.m_storage)
, m_control_block(other.m_control_block)
{
@@ -64,10 +99,10 @@ namespace kstd
}
}
- /**
- * @brief Copy constructor. Constructs a weak_ptr which shares ownership of the object managed by other.
- */
- weak_ptr(weak_ptr const & other)
+ //! Construct a weak pointer which shares ownership of the object managed by other.
+ template<typename Y>
+ requires bits::is_shared_pointer_ctor_compatible<Y *, T *>::value
+ constexpr weak_ptr(weak_ptr<Y> const & other)
: m_storage(other.m_storage)
, m_control_block(other.m_control_block)
{
@@ -77,86 +112,149 @@ namespace kstd
}
}
- /**
- * @brief Move constructor. Constructs a weak_ptr which takes ownership of the object managed by other.
- */
- weak_ptr(weak_ptr && other) noexcept
+ //! Construct a weak pointer which shares ownership of the object managed by other.
+ template<typename Y>
+ requires bits::is_shared_pointer_ctor_compatible<Y *, T *>::value
+ constexpr weak_ptr(shared_ptr<Y> const & other)
: m_storage(other.m_storage)
, m_control_block(other.m_control_block)
{
- other.m_storage = nullptr;
- other.m_control_block = nullptr;
- }
-
- /**
- * @brief Assignment operator. Assigns the weak_ptr to another weak_ptr.
- */
- auto operator=(weak_ptr const & other) -> weak_ptr &
- {
- if (this != &other)
+ if (m_control_block != nullptr)
{
- cleanup();
- m_storage = other.m_storage;
- m_control_block = other.m_control_block;
- if (m_control_block != nullptr)
- {
- ++(m_control_block->weak_count);
- }
+ ++(m_control_block->weak_count);
}
+ }
+
+ //! Construct a weak pointer which takes ownership of the object managed by other.
+ constexpr weak_ptr(weak_ptr && other) noexcept
+ : m_storage(std::exchange(other.m_storage, nullptr))
+ , m_control_block(std::exchange(other.m_control_block, nullptr))
+ {}
+
+ //! Construct a weak pointer which takes ownership of the object managed by other.
+ template<typename Y>
+ requires std::is_convertible_v<Y *, T *>
+ constexpr weak_ptr(weak_ptr<Y> && other) noexcept
+ : m_storage(std::exchange(other.m_storage, nullptr))
+ , m_control_block(std::exchange(other.m_control_block, nullptr))
+ {}
+ //! Replace the object managed by this pointer with the one managed by another one.
+ auto operator=(weak_ptr const & other) noexcept -> weak_ptr &
+ {
+ weak_ptr<T>{other}.swap(*this);
return *this;
}
- /**
- * @brief Move assignment operator. Move-assigns a weak_ptr from other.
- */
- auto operator=(weak_ptr && other) noexcept -> weak_ptr &
+ //! Replace the object managed by this pointer with the one managed by another one.
+ template<typename Y>
+ constexpr auto operator=(weak_ptr<Y> const & other) noexcept -> weak_ptr &
{
- if (this != &other)
- {
- cleanup();
- m_storage = other.m_storage;
- m_control_block = other.m_control_block;
- other.m_storage = nullptr;
- other.m_control_block = nullptr;
- }
+ weak_ptr<T>{other}.swap(*this);
+ return *this;
+ }
+ //! Replace the object managed by this pointer with the one managed by shared pointer.
+ template<typename Y>
+ constexpr auto operator=(shared_ptr<Y> const & other) noexcept -> weak_ptr &
+ {
+ weak_ptr<T>{other}.swap(*this);
return *this;
}
- /**
- * @brief Destructor. Cleans up resources if necessary.
- */
- ~weak_ptr()
+ //! Replace the object managed by this pointer with the one managed by another one.
+ auto operator=(weak_ptr && other) noexcept -> weak_ptr &
{
- cleanup();
+ weak_ptr<T>{std::move(other)}.swap(*this);
+ return *this;
}
- /**
- * @brief Returns a shared_ptr that shares ownership of the managed object if the managed object still exists, or an
- * empty shared_ptr otherwise.
- */
- [[nodiscard]] auto lock() const -> shared_ptr<T>
+ //! Replace the object managed by this pointer with the one managed by another one.
+ template<typename Y>
+ auto operator=(weak_ptr<Y> && other) noexcept -> weak_ptr &
{
- return shared_ptr<T>(*this);
+ weak_ptr<T>{std::move(other)}.swap(*this);
+ return *this;
}
- private:
- auto cleanup() -> void
+ //! Destroy this pointer, cleaning up resources if necessary.
+ ~weak_ptr()
{
- if (m_control_block != nullptr)
+ if (m_control_block)
{
- if (--(m_control_block->weak_count) == 0)
+ if (!m_control_block->weak_count.fetch_sub(1))
{
delete m_control_block;
+ m_control_block = nullptr;
+ m_storage = nullptr;
}
}
}
+ //! Release the reference to the object managed by this weak pointer.
+ constexpr auto reset() -> void
+ {
+ weak_ptr<T>{}.swap(*this);
+ }
+
+ //! Exchange the ownership of the object managed by this pointer with the one of another one.
+ constexpr auto swap(weak_ptr & other) -> void
+ {
+ std::ranges::swap(m_storage, other.m_storage);
+ std::ranges::swap(m_control_block, other.m_control_block);
+ }
+
+ //! Exchange the ownership of the object managed by weak pointer objects.
+ constexpr auto friend swap(weak_ptr & lhs, weak_ptr & rhs) noexcept -> void
+ {
+ return lhs.swap(rhs);
+ }
+
+ //! Get the number of shared pointers that share ownership with the object managed by this weak pointer.
+ [[nodiscard]] constexpr auto use_count() const noexcept -> long
+ {
+ if (m_control_block)
+ {
+ return m_control_block->strong_count;
+ }
+ return 0;
+ }
+
+ //! Check if the object managed by this weak pointer has already been deleted.
+ [[nodiscard]] constexpr auto expired() const noexcept -> bool
+ {
+ return use_count() == 0;
+ }
+
+ //! Create a shared pointer that manages the object, if any, managed by this weak pointer.
+ [[nodiscard]] auto lock() const -> shared_ptr<T>
+ {
+ return expired() ? shared_ptr<T>{} : shared_ptr<T>(*this);
+ }
+
+ //! Check if this weak pointer precedes another one in owner-based order.
+ template<typename Y>
+ [[nodiscard]] auto owner_before(weak_ptr<Y> const & other) const noexcept -> bool
+ {
+ return m_control_block < other.m_control_block;
+ }
+
+ //! Check if this weak pointer precedes a shared pointer in owner-based order.
+ template<typename Y>
+ [[nodiscard]] auto owner_before(shared_ptr<Y> const & other) const noexcept -> bool
+ {
+ return m_control_block < other.m_control_block;
+ }
+
+ private:
T * m_storage;
- shared_control_block * m_control_block;
+ bits::shared_control_block * m_control_block;
};
+ //! Deduction guide for construction from a shared pointer.
+ template<typename T>
+ weak_ptr(shared_ptr<T>) -> weak_ptr<T>;
+
/**
* @brief enable_shared_from_this is a base class that allows an object that is currently managed by a shared_ptr to
* create additional shared_ptr instances that share ownership of the same object. This is usefl when you want to
@@ -249,7 +347,7 @@ namespace kstd
requires(std::is_convertible_v<U *, T *>)
explicit shared_ptr(U * pointer = nullptr)
: m_storage(pointer)
- , m_control_block(pointer != nullptr ? new shared_control_block() : nullptr)
+ , m_control_block(pointer != nullptr ? new bits::shared_control_block() : nullptr)
{
assign_enable_shared_from_this(pointer);
}
@@ -268,8 +366,8 @@ namespace kstd
{
if (other.m_control_block)
{
- auto count = other.m_control_block->shared_count.load(std::memory_order::relaxed);
- while (count != 0 && !other.m_control_block->shared_count.compare_exchange_weak(
+ auto count = other.m_control_block->strong_count.load(std::memory_order::relaxed);
+ while (count != 0 && !other.m_control_block->strong_count.compare_exchange_weak(
count, count + 1, std::memory_order::acquire, std::memory_order::relaxed))
;
@@ -292,7 +390,7 @@ namespace kstd
{
if (m_control_block != nullptr)
{
- ++(m_control_block->shared_count);
+ ++(m_control_block->strong_count);
}
}
@@ -310,7 +408,7 @@ namespace kstd
{
if (m_control_block != nullptr)
{
- ++(m_control_block->shared_count);
+ ++(m_control_block->strong_count);
}
}
@@ -361,7 +459,7 @@ namespace kstd
if (m_control_block != nullptr)
{
- ++(m_control_block->shared_count);
+ ++(m_control_block->strong_count);
}
}
@@ -385,7 +483,7 @@ namespace kstd
if (m_control_block != nullptr)
{
- ++(m_control_block->shared_count);
+ ++(m_control_block->strong_count);
}
return *this;
@@ -460,7 +558,7 @@ namespace kstd
{
cleanup();
m_storage = ptr;
- m_control_block = ptr != nullptr ? new shared_control_block() : nullptr;
+ m_control_block = ptr != nullptr ? new bits::shared_control_block() : nullptr;
assign_enable_shared_from_this(ptr);
}
@@ -521,7 +619,7 @@ namespace kstd
{
if (m_control_block != nullptr)
{
- return m_control_block->shared_count;
+ return m_control_block->strong_count;
}
return 0;
@@ -581,7 +679,7 @@ namespace kstd
{
if (m_control_block != nullptr)
{
- if (--(m_control_block->shared_count) == 0)
+ if (--(m_control_block->strong_count) == 0)
{
delete m_storage;
m_storage = nullptr;
@@ -594,8 +692,8 @@ namespace kstd
}
}
- T * m_storage; ///< The managed object.
- shared_control_block * m_control_block; ///< Shared control block.
+ T * m_storage; ///< The managed object.
+ bits::shared_control_block * m_control_block; ///< Shared control block.
};
/**