diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2024-05-04 22:57:32 +0200 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2024-05-05 03:31:20 +0200 |
commit | 92d60403f3de094dc2d040a7fce869a5bb60695a (patch) | |
tree | 9f990e482ea22a761af8d2d7a04aae653ccb4204 | |
parent | b929d0a245219247c81bab7d4d319b5ad9ad58f7 (diff) |
a
-rw-r--r-- | compat/borrowed-ptr-fwd.hpp | 42 | ||||
-rw-r--r-- | compat/borrowed-ptr.cpp | 2 | ||||
-rw-r--r-- | compat/borrowed-ptr.inl | 84 | ||||
-rw-r--r-- | test/bptr.cpp | 52 |
4 files changed, 137 insertions, 43 deletions
diff --git a/compat/borrowed-ptr-fwd.hpp b/compat/borrowed-ptr-fwd.hpp index 465e9bf4..01dd20f7 100644 --- a/compat/borrowed-ptr-fwd.hpp +++ b/compat/borrowed-ptr-fwd.hpp @@ -1,6 +1,15 @@ #pragma once -namespace floormat::detail_borrowed_ptr { struct control_block_; } +namespace floormat::detail_borrowed_ptr { + +struct control_block; +template<typename From, typename To> +concept DerivedFrom = requires(From* x) { + requires !std::is_same_v<From, To>; + requires std::is_nothrow_convertible_v<From*, To*>; +}; + +} // namespace floormat::detail_borrowed_ptr namespace floormat { @@ -11,26 +20,39 @@ template<typename T> class bptr final // NOLINT(*-special-member-functions) { mutable T* casted_ptr; - detail_borrowed_ptr::control_block_* blk; + detail_borrowed_ptr::control_block* blk; - explicit bptr(DirectInitT, T* casted_ptr, detail_borrowed_ptr::control_block_* blk) noexcept; - //explicit bptr(NoInitT) noexcept; + explicit bptr(DirectInitT, T* casted_ptr, detail_borrowed_ptr::control_block* blk) noexcept; + + struct private_tag_t final {}; + static constexpr private_tag_t private_tag{}; + + template<typename Y> bptr(const bptr<Y>& other, private_tag_t) noexcept; + template<typename Y> bptr& _copy_assign(const bptr<Y>& other) noexcept; + template<typename Y> bptr(bptr<Y>&& other, private_tag_t) noexcept; + template<typename Y> bptr& _move_assign(bptr<Y>&& other) noexcept; public: template<typename... Ts> requires std::is_constructible_v<T, Ts&&...> explicit bptr(InPlaceInitT, Ts&&... args) noexcept; - bptr(std::nullptr_t) noexcept; // NOLINT(*-explicit-conversions) - bptr() noexcept; explicit bptr(T* ptr) noexcept; + bptr() noexcept; ~bptr() noexcept; + bptr(std::nullptr_t) noexcept; // NOLINT(*-explicit-conversions) bptr& operator=(std::nullptr_t) noexcept; - template<typename Y> requires std::is_convertible_v<Y*, T*> bptr(const bptr<Y>&) noexcept; - template<typename Y> requires std::is_convertible_v<Y*, T*> bptr& operator=(const bptr<Y>&) noexcept; - template<typename Y> requires std::is_convertible_v<Y*, T*> bptr(bptr<Y>&&) noexcept; - template<typename Y> requires std::is_convertible_v<Y*, T*> bptr& operator=(bptr<Y>&&) noexcept; + + bptr(const bptr&) noexcept; + bptr& operator=(const bptr&) noexcept; + template<detail_borrowed_ptr::DerivedFrom<T> Y> bptr(const bptr<Y>&) noexcept; + template<detail_borrowed_ptr::DerivedFrom<T> Y> bptr& operator=(const bptr<Y>&) noexcept; + + bptr(bptr&&) noexcept; + bptr& operator=(bptr&&) noexcept; + template<detail_borrowed_ptr::DerivedFrom<T> Y> bptr(bptr<Y>&&) noexcept; + template<detail_borrowed_ptr::DerivedFrom<T> Y> bptr& operator=(bptr<Y>&&) noexcept; friend bool operator==<T>(const bptr<T>& a, const bptr<T>& b) noexcept; explicit operator bool() const noexcept; diff --git a/compat/borrowed-ptr.cpp b/compat/borrowed-ptr.cpp index 413527cb..a73b869f 100644 --- a/compat/borrowed-ptr.cpp +++ b/compat/borrowed-ptr.cpp @@ -13,7 +13,7 @@ namespace floormat::detail_borrowed_ptr { #pragma warning(push) #pragma warning(disable : 5205) #endif -void control_block_::decrement(control_block_*& blk) noexcept +void control_block::decrement(control_block*& blk) noexcept { fm_bptr_assert(blk); auto c = --blk->_count; diff --git a/compat/borrowed-ptr.inl b/compat/borrowed-ptr.inl index fd2d25d2..98e0fec5 100644 --- a/compat/borrowed-ptr.inl +++ b/compat/borrowed-ptr.inl @@ -1,6 +1,7 @@ #pragma once #include "borrowed-ptr.hpp" #include "compat/assert.hpp" +#include <utility> #define FM_BPTR_DEBUG @@ -21,22 +22,22 @@ namespace floormat::detail_borrowed_ptr { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" #endif -struct control_block_ +struct control_block { void* _ptr; // todo maybe add directly embeddable objects? uint32_t _count; virtual void free_ptr() noexcept = 0; - static void decrement(control_block_*& blk) noexcept; + static void decrement(control_block*& blk) noexcept; }; #ifdef __GNUG__ #pragma GCC diagnostic pop #endif template<typename T> -struct control_block_impl final: control_block_ +struct control_block_impl final: control_block { void free_ptr() noexcept override; - [[nodiscard]] static control_block_* create(T* ptr) noexcept; + [[nodiscard]] static control_block* create(T* ptr) noexcept; protected: explicit control_block_impl(T* ptr) noexcept; }; @@ -56,7 +57,7 @@ void control_block_impl<T>::free_ptr() noexcept } template <typename T> -control_block_* control_block_impl<T>::create(T* ptr) noexcept +control_block* control_block_impl<T>::create(T* ptr) noexcept { if (ptr) { @@ -71,12 +72,10 @@ control_block_* control_block_impl<T>::create(T* ptr) noexcept namespace floormat { -template<typename T> bptr<T>::bptr(DirectInitT, T* casted_ptr, detail_borrowed_ptr::control_block_* blk) noexcept: +template<typename T> bptr<T>::bptr(DirectInitT, T* casted_ptr, detail_borrowed_ptr::control_block* blk) noexcept: casted_ptr{casted_ptr}, blk{blk} {} -//template<typename T> bptr<T>::bptr(NoInitT) noexcept {} - template<typename T> template<typename... Ts> requires std::is_constructible_v<T, Ts&&...> @@ -103,28 +102,29 @@ bptr<T>::~bptr() noexcept blk->decrement(blk); } +template<typename T> bptr<T>::bptr(const bptr& other) noexcept: bptr{other, private_tag} {} +template<typename T> bptr<T>& bptr<T>::operator=(const bptr& other) noexcept { return _copy_assign(other); } + template<typename T> -template<typename Y> -requires std::is_convertible_v<Y*, T*> -bptr<T>::bptr(const bptr<Y>& other) noexcept: - casted_ptr{other.casted_ptr}, blk{other.blk} -{ - if (blk) - { - ++blk->_count; - fm_bptr_assert(blk->_count > 1); - } -} +template<detail_borrowed_ptr::DerivedFrom<T> Y> +bptr<T>::bptr(const bptr<Y>& other) noexcept: bptr{other, private_tag} {} template<typename T> -template<typename Y> -requires std::is_convertible_v<Y*, T*> -bptr<T>::bptr(bptr<Y>&& other) noexcept: - casted_ptr{other.casted_ptr}, blk{other.blk} -{ - other.casted_ptr = nullptr; - other.blk = nullptr; -} +template<detail_borrowed_ptr::DerivedFrom<T> Y> +bptr<T>& bptr<T>::operator=(const bptr<Y>& other) noexcept +{ return _copy_assign(other); } + +template<typename T> bptr<T>& bptr<T>::operator=(bptr&& other) noexcept { return _move_assign(move(other)); } +template<typename T> bptr<T>::bptr(bptr&& other) noexcept: bptr{move(other), private_tag} {} + +template<typename T> +template<detail_borrowed_ptr::DerivedFrom<T> Y> +bptr<T>::bptr(bptr<Y>&& other) noexcept: bptr{move(other), private_tag} {} + +template<typename T> +template<detail_borrowed_ptr::DerivedFrom<T> Y> +bptr<T>& bptr<T>::operator=(bptr<Y>&& other) noexcept +{ return _move_assign(move(other)); } template<typename T> void bptr<T>::reset() noexcept @@ -153,8 +153,19 @@ template<typename T> bptr<T>& bptr<T>::operator=(std::nullptr_t) noexcept { rese template<typename T> template<typename Y> -requires std::is_convertible_v<Y*, T*> -bptr<T>& bptr<T>::operator=(const bptr<Y>& other) noexcept +bptr<T>::bptr(const bptr<Y>& other, private_tag_t) noexcept: + casted_ptr{other.casted_ptr}, blk{other.blk} +{ + if (blk) + { + ++blk->_count; + fm_bptr_assert(blk->_count > 1); + } +} + +template<typename T> +template<typename Y> +bptr<T>& bptr<T>::_copy_assign(const bptr<Y>& other) noexcept { if (blk != other.blk) { @@ -170,10 +181,19 @@ bptr<T>& bptr<T>::operator=(const bptr<Y>& other) noexcept template<typename T> template<typename Y> -requires std::is_convertible_v<Y*, T*> -bptr<T>& bptr<T>::operator=(bptr<Y>&& other) noexcept +bptr<T>::bptr(bptr<Y>&& other, private_tag_t) noexcept: + casted_ptr{other.casted_ptr}, blk{other.blk} { - blk->decrement(blk); + other.casted_ptr = nullptr; + other.blk = nullptr; +} + +template<typename T> +template<typename Y> +bptr<T>& bptr<T>::_move_assign(bptr<Y>&& other) noexcept +{ + if (blk) + blk->decrement(blk); casted_ptr = other.casted_ptr; blk = other.blk; other.casted_ptr = nullptr; diff --git a/test/bptr.cpp b/test/bptr.cpp index 99d6cd2c..6f823344 100644 --- a/test/bptr.cpp +++ b/test/bptr.cpp @@ -39,8 +39,59 @@ template bptr<Foo> static_pointer_cast(const bptr<Bar>&) noexcept; namespace { +int A_total = 0, A_alive = 0; // NOLINT + +struct A +{ + int val, serial; + explicit A(int val) : val{val}, serial{++A_total} { ++A_alive; } + ~A() noexcept { --A_alive; fm_assert(A_alive >= 0); } +}; + void test1() { + A_total = 0; A_alive = 0; + + auto p1 = bptr<A>{InPlace, 1}; + fm_assert(p1.use_count() == 1); + fm_assert(p1.get()); + fm_assert(A_total == 1); + fm_assert(A_alive == 1); + + p1 = nullptr; + fm_assert(p1.use_count() == 0); + fm_assert(!p1.get()); + fm_assert(A_total == 1); + fm_assert(A_alive == 0); +} + +void test2() +{ + A_total = 0; A_alive = 0; + + auto p1 = bptr<A>{InPlace, 2}; + auto p2 = p1; + + fm_assert(p1.get()); + fm_assert(p1.get() == p2.get()); + fm_assert(p1->val == 2); + fm_assert(p1->serial == 1); + fm_assert(A_total == 1); + fm_assert(A_alive == 1); + + p1 = nullptr; + fm_assert(!p1.get()); + fm_assert(p2.get()); + fm_assert(p2->val == 2); + fm_assert(p2->serial == 1); + fm_assert(A_total == 1); + fm_assert(A_alive == 1); + + p2 = nullptr; + fm_assert(!p1.get()); + fm_assert(!p2.get()); + fm_assert(A_total == 1); + fm_assert(A_alive == 0); } } // namespace @@ -48,6 +99,7 @@ void test1() void test_app::test_bptr() { test1(); + test2(); } } // namespace floormat |