aboutsummaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorLukas Oesch <lukasoesch20@gmail.com>2026-03-17 11:48:40 +0100
committerLukas Oesch <lukasoesch20@gmail.com>2026-03-17 16:44:34 +0100
commit471888c64ed490b1f1dbaa2c2f67a1e8d315905a (patch)
tree0d41a93c517afe7cd4228ef44c1f34ec9a1e484c /libs
parent9391c5881a8a635677312df80888e972d7175cfa (diff)
downloadteachos-471888c64ed490b1f1dbaa2c2f67a1e8d315905a.tar.xz
teachos-471888c64ed490b1f1dbaa2c2f67a1e8d315905a.zip
extend shared_ptr to support nullptr and cross-type conversions
Diffstat (limited to 'libs')
-rw-r--r--libs/kstd/include/kstd/bits/shared_ptr.hpp131
1 files changed, 125 insertions, 6 deletions
diff --git a/libs/kstd/include/kstd/bits/shared_ptr.hpp b/libs/kstd/include/kstd/bits/shared_ptr.hpp
index cfe5d18..ed23d29 100644
--- a/libs/kstd/include/kstd/bits/shared_ptr.hpp
+++ b/libs/kstd/include/kstd/bits/shared_ptr.hpp
@@ -3,12 +3,16 @@
#include <atomic>
#include <cstddef>
+#include <type_traits>
#include <utility>
// IWYU pragma: private, include <kstd/memory>
namespace kstd
{
+ template<typename T>
+ struct shared_ptr;
+
/**
* @brief Shared_pointer is a smart pointer that retains shared ownership of an object through a pointer. Several
* shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of
@@ -24,6 +28,17 @@ namespace kstd
template<typename T>
struct shared_ptr
{
+ template<typename U>
+ friend struct shared_ptr;
+
+ /**
+ * @brief Construct an empty shared_ptr from nullptr.
+ */
+ shared_ptr(std::nullptr_t) noexcept
+ : pointer(nullptr)
+ , ref_count(nullptr)
+ {}
+
/**
* @brief Constructor.
*
@@ -31,7 +46,7 @@ namespace kstd
*/
explicit shared_ptr(T * pointer = nullptr)
: pointer(pointer)
- , ref_count(new std::atomic<std::size_t>(pointer != nullptr ? 1 : 0))
+ , ref_count(pointer != nullptr ? new std::atomic<std::size_t>(1) : nullptr)
{
// Nothing to do.
}
@@ -45,7 +60,25 @@ namespace kstd
: pointer(other.pointer)
, ref_count(other.ref_count)
{
- if (pointer != nullptr)
+ if (ref_count != nullptr)
+ {
+ ++(*ref_count);
+ }
+ }
+
+ /**
+ * @brief Converting copy constructor for compatible shared_ptr types.
+ *
+ * @tparam U Source pointer element type.
+ * @param other The shared_ptr to copy from.
+ */
+ template<typename U>
+ requires(std::is_convertible_v<U *, T *>)
+ shared_ptr(shared_ptr<U> const & other)
+ : pointer(other.pointer)
+ , ref_count(other.ref_count)
+ {
+ if (ref_count != nullptr)
{
++(*ref_count);
}
@@ -65,6 +98,22 @@ namespace kstd
}
/**
+ * @brief Converting move constructor for compatible shared_ptr types.
+ *
+ * @tparam U Source pointer element type.
+ * @param other The shared_ptr to move from.
+ */
+ template<typename U>
+ requires(std::is_convertible_v<U *, T *>)
+ shared_ptr(shared_ptr<U> && other) noexcept
+ : pointer(other.pointer)
+ , ref_count(other.ref_count)
+ {
+ other.pointer = nullptr;
+ other.ref_count = nullptr;
+ }
+
+ /**
* @brief Copy assignment operator. Replaces the managed object with the one managed by r. Shares ownership of the
* object managed by r. If r manages no object, *this manages no object too. Equivalent to
* shared_ptr<T>(r).swap(*this).
@@ -80,7 +129,7 @@ namespace kstd
pointer = other.pointer;
ref_count = other.ref_count;
- if (pointer != nullptr)
+ if (ref_count != nullptr)
{
++(*ref_count);
}
@@ -90,6 +139,29 @@ namespace kstd
}
/**
+ * @brief Converting copy assignment for compatible shared_ptr types.
+ *
+ * @tparam U Source pointer element type.
+ * @param other Another smart pointer to share ownership with.
+ * @return Reference to this shared pointer.
+ */
+ template<typename U>
+ requires(std::is_convertible_v<U *, T *>)
+ auto operator=(shared_ptr<U> const & other) -> shared_ptr &
+ {
+ cleanup();
+ pointer = other.pointer;
+ ref_count = other.ref_count;
+
+ if (ref_count != nullptr)
+ {
+ ++(*ref_count);
+ }
+
+ return *this;
+ }
+
+ /**
* @brief Move assignment operator. Move-assigns a shared_ptr from r. After the assignment, *this contains a copy of
* the previous state of r, and r is empty. Equivalent to shared_ptr<T>(std::move(r)).swap(*this).
*
@@ -111,6 +183,37 @@ namespace kstd
}
/**
+ * @brief Converting move assignment for compatible shared_ptr types.
+ *
+ * @tparam U Source pointer element type.
+ * @param other Another smart pointer to acquire ownership from.
+ * @return Reference to this shared pointer.
+ */
+ template<typename U>
+ requires(std::is_convertible_v<U *, T *>)
+ auto operator=(shared_ptr<U> && other) noexcept -> shared_ptr &
+ {
+ cleanup();
+ pointer = other.pointer;
+ ref_count = other.ref_count;
+ other.pointer = nullptr;
+ other.ref_count = nullptr;
+
+ return *this;
+ }
+
+ /**
+ * @brief Reset this shared_ptr to empty via nullptr assignment.
+ */
+ auto operator=(std::nullptr_t) noexcept -> shared_ptr &
+ {
+ cleanup();
+ pointer = nullptr;
+ ref_count = nullptr;
+ return *this;
+ }
+
+ /**
* @brief Destructor. Cleans up resources if necessary.
*/
~shared_ptr()
@@ -127,7 +230,7 @@ namespace kstd
{
cleanup();
pointer = ptr;
- ref_count = new std::atomic<std::size_t>(ptr != nullptr ? 1 : 0);
+ ref_count = ptr != nullptr ? new std::atomic<std::size_t>(1) : nullptr;
}
/**
@@ -185,7 +288,7 @@ namespace kstd
*/
[[nodiscard]] auto use_count() const -> std::size_t
{
- if (pointer != nullptr)
+ if (ref_count != nullptr)
{
return *ref_count;
}
@@ -204,6 +307,22 @@ namespace kstd
}
/**
+ * @brief Compare shared_ptr with nullptr.
+ */
+ [[nodiscard]] auto operator==(std::nullptr_t) const -> bool
+ {
+ return pointer == nullptr;
+ }
+
+ /**
+ * @brief Compare nullptr with shared_ptr.
+ */
+ friend auto operator==(std::nullptr_t, shared_ptr const & ptr) -> bool
+ {
+ return ptr.pointer == nullptr;
+ }
+
+ /**
* @brief Defaulted three-way comparator operator.
*/
auto operator<=>(shared_ptr const & other) const = default;
@@ -214,7 +333,7 @@ namespace kstd
*/
auto cleanup() -> void
{
- if (pointer != nullptr && ref_count != nullptr && --(*ref_count) == 0)
+ if (ref_count != nullptr && --(*ref_count) == 0)
{
delete pointer;
delete ref_count;