From 270ba25a01bfa19f3511bd70f994cfbd6473863d Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Tue, 6 Feb 2024 08:15:34 +0100 Subject: compat/intrusive-ptr: WIP --- compat/intrusive-ptr.hpp | 326 +++++++++++++++++++++++++++++++++++++++++++++++ compat/intrusive-ptr.inl | 8 ++ 2 files changed, 334 insertions(+) create mode 100644 compat/intrusive-ptr.hpp create mode 100644 compat/intrusive-ptr.inl (limited to 'compat') diff --git a/compat/intrusive-ptr.hpp b/compat/intrusive-ptr.hpp new file mode 100644 index 00000000..0d09a366 --- /dev/null +++ b/compat/intrusive-ptr.hpp @@ -0,0 +1,326 @@ +#pragma once +#include "compat/assert.hpp" +#include +#include + +namespace floormat::iptr { struct non_atomic_u32_tag{}; } + +namespace floormat { + +template class basic_iptr; +template using local_iptr = basic_iptr; + +} // namespace floormat + +namespace floormat::iptr { + +template struct refcount_traits; +template struct refcount_access; +template struct refcount_ops; + +template +struct refcount_traits +{ + refcount_traits() = delete; + using counter_type = uint32_t; + using size_type = size_t; + using access_t = refcount_access; +}; + +template +struct refcount_ops +{ + refcount_ops() = delete; + + using traits = refcount_traits; + using counter_type = typename traits::counter_type; + using size_type = typename traits::size_type; + using access_t = typename traits::access_t; + +#ifndef FM_NO_DEBUG + using Tref = T*&; +#else + using Tref = T*; +#endif + + static constexpr inline auto incr(T* ptr) noexcept -> size_type; + static constexpr inline auto decr(Tref ptr) noexcept -> size_type; + static constexpr inline auto count(T* ptr) noexcept -> size_type; + static constexpr inline void init_to_1(T* ptr) noexcept; +}; + +template +struct refcount_access +{ + using counter_type = refcount_traits::counter_type; + refcount_access() = delete; + + static constexpr auto access(T* ptr) noexcept -> counter_type&; + template static constexpr Y* checked_cast(const T* ptr) noexcept; // todo! +}; + +template +constexpr auto refcount_ops::incr(T* ptr) noexcept -> size_type +{ + fm_debug_assert(ptr != nullptr); + counter_type& ctr{access_t::access(ptr)}; + fm_debug_assert(size_type{ctr} > size_type{0}); + size_type ret{++ctr}; + return ret; +} + +template +constexpr auto refcount_ops::decr(Tref ptr) noexcept -> size_type +{ + static_assert(std::is_nothrow_destructible_v); + + fm_debug_assert(ptr != nullptr); + counter_type& ctr{access_t::access(ptr)}; + fm_debug_assert(size_type{ctr} > size_type{0}); + size_type ret{--ctr}; + if (ret == size_type{0}) + { + delete ptr; + if constexpr(std::is_reference_v) + ptr = reinterpret_cast((uintptr_t)-1); + } + return ret; +} + +template +constexpr auto refcount_ops::count(T* ptr) noexcept -> size_type +{ + if (!CORRADE_CONSTEVAL) + fm_debug_assert((void*)ptr != (void*)-1); + if (!ptr) [[unlikely]] + return 0; + counter_type& ctr{access_t::access(ptr)}; + fm_debug_assert(size_type{ctr} > size_type{0}); + size_type ret{ctr}; + return ret; +} + +template +constexpr void refcount_ops::init_to_1(T* ptr) noexcept +{ + fm_debug_assert(ptr != nullptr); + counter_type& ctr{access_t::access(ptr)}; + fm_debug_assert(size_type{ctr} == size_type{0}); + ctr = size_type{1}; +} + +template +consteval bool check_traits() +{ + using traits = ::floormat::iptr::refcount_traits; + using counter_type = typename traits::counter_type; + using size_type = typename traits::size_type; + using access_t = ::floormat::iptr::refcount_access; + using ops_t = ::floormat::iptr::refcount_ops; + + static_assert(requires (counter_type& ctr) { + requires std::is_arithmetic_v; + requires std::is_unsigned_v; + requires std::is_fundamental_v; + requires noexcept(size_type{ctr}); + { size_t{size_type{ctr}} }; + { ctr = size_type{1} }; + { size_type{++ctr} }; + { size_type{--ctr} }; + requires noexcept(size_t{size_type{ctr}}); + requires noexcept(ctr = size_type{1}); + requires noexcept(size_type{++ctr}); + requires noexcept(size_type{--ctr}); + requires std::is_nothrow_destructible_v; + requires sizeof(access_t) != 0; + requires sizeof(ops_t) != 0; + }); + + return true; +} + +} // namespace floormat::iptr + + + +// ----- macros ----- + +#define fm_template template +#define fm_basic_iptr basic_iptr + +#ifndef FM_IPTR_USE_CONSTRUCT_AT // todo! finish it +#define FM_IPTR_USE_CONSTRUCT_AT 0 +#endif + +// ----- basic_iptr ----- + +namespace floormat { + +template +class basic_iptr final +{ + static_assert(!std::is_reference_v); + static_assert(!std::is_const_v); // todo, modify only refcount + + using traits = ::floormat::iptr::refcount_traits; + using counter_type = typename traits::counter_type; + using size_type = typename traits::size_type; + using access_t = ::floormat::iptr::refcount_access; + using ops_t = ::floormat::iptr::refcount_ops; + static_assert(::floormat::iptr::check_traits()); + + T* _ptr{nullptr}; + + // todo use std::construct_at as it has a constexpr exception. + // ...but it requires :( — maybe use an ifdef? + +public: + constexpr basic_iptr() noexcept; + constexpr basic_iptr(std::nullptr_t) noexcept; + constexpr basic_iptr& operator=(std::nullptr_t) noexcept; + explicit constexpr basic_iptr(T* ptr) noexcept; + constexpr basic_iptr(basic_iptr&& other) noexcept; + constexpr basic_iptr(const basic_iptr& other) noexcept; + constexpr basic_iptr& operator=(basic_iptr&& other) noexcept; + constexpr basic_iptr& operator=(const basic_iptr& other) noexcept; + constexpr ~basic_iptr() noexcept; + + template requires std::is_constructible_v + constexpr basic_iptr(InPlaceInitT, Ts&&... args) // NOLINT(*-missing-std-forward) + noexcept(std::is_nothrow_constructible_v); + + constexpr inline void reset() noexcept; + constexpr void reset(T* ptr) noexcept; // todo casts + constexpr void swap(basic_iptr& other) noexcept; + constexpr T* get() const noexcept; + constexpr inline T& operator*() const noexcept; + constexpr inline T* operator->() const noexcept; + + constexpr size_type use_count() const noexcept; + explicit constexpr operator bool() const noexcept; + + template + friend constexpr bool operator==(const basic_iptr& a, const basic_iptr& b) noexcept; + + template + friend constexpr std::strong_ordering operator<=>(const basic_iptr& a, const basic_iptr& b) noexcept; +}; + +// ----- constructors ----- + +template constexpr fm_basic_iptr::basic_iptr() noexcept = default; +template constexpr fm_basic_iptr::basic_iptr(std::nullptr_t) noexcept {} + +fm_template constexpr fm_basic_iptr& fm_basic_iptr::operator=(std::nullptr_t) noexcept +{ + if (_ptr) + { + ops_t::decr(_ptr); + _ptr = nullptr; + } + return *this; +} + +fm_template constexpr fm_basic_iptr::basic_iptr(fm_basic_iptr&& other) noexcept: _ptr{other._ptr} { other._ptr = nullptr; } + +fm_template constexpr fm_basic_iptr::basic_iptr(const fm_basic_iptr& other) noexcept: + _ptr{other._ptr} +{ + if (_ptr) + ops_t::incr(_ptr); +} + +fm_template constexpr fm_basic_iptr::basic_iptr(T* ptr) noexcept +{ + ops_t::init_to_1(ptr); +} + +// ----- destructor ----- + +fm_template constexpr fm_basic_iptr::~basic_iptr() noexcept { reset(); } + +// ----- assignment operators ----- + +fm_template constexpr fm_basic_iptr& fm_basic_iptr::operator=(fm_basic_iptr&& other) noexcept +{ + if (_ptr != other._ptr) + { + reset(); + _ptr = other._ptr; + other._ptr = nullptr; + } + return *this; +} + +fm_template constexpr fm_basic_iptr& fm_basic_iptr::operator=(const fm_basic_iptr& other) noexcept +{ +#ifdef __CLION_IDE__ + if (&other == this) + return *this; +#endif + if (other._ptr != _ptr) [[likely]] + { + if (_ptr) + ops_t::decr(_ptr); + _ptr = other._ptr; + if (_ptr) + ops_t::incr(_ptr); + } + return *this; +} + +fm_template template +requires std::is_constructible_v +constexpr fm_basic_iptr::basic_iptr(InPlaceInitT, Ts&&... args) // NOLINT(*-missing-std-forward) +noexcept(std::is_nothrow_constructible_v): + _ptr{new T{Utility::forward(args...)}} +{ + ops_t::init_to_1(_ptr); +} + +fm_template constexpr void fm_basic_iptr::reset() noexcept { if (_ptr) ops_t::decr(_ptr); } +fm_template constexpr void fm_basic_iptr::reset(T* ptr) noexcept { reset(); _ptr = ptr; } +fm_template constexpr void fm_basic_iptr::swap(basic_iptr& other) noexcept { auto p = _ptr; _ptr = other._ptr; other._ptr = p; } + +fm_template constexpr T* fm_basic_iptr::get() const noexcept +{ + fm_debug_assert((void*)_ptr != (void*)-1); // NOLINT(*-no-int-to-ptr) + return _ptr; +} + +fm_template constexpr T& fm_basic_iptr::operator*() const noexcept { return *get(); } +fm_template constexpr T* fm_basic_iptr::operator->() const noexcept { return get(); } + +fm_template constexpr auto fm_basic_iptr::use_count() const noexcept -> size_type +{ + if (!CORRADE_CONSTEVAL) + fm_debug_assert((void*)_ptr != (void*)-1); + return ops_t::count(_ptr); +} + +fm_template constexpr fm_basic_iptr::operator bool() const noexcept +{ + if (!CORRADE_CONSTEVAL) + fm_debug_assert((void*)_ptr != (void*)-1); + return _ptr != nullptr; +} + +fm_template constexpr bool operator==(const fm_basic_iptr& a, const fm_basic_iptr& b) noexcept +{ + if (!CORRADE_CONSTEVAL) + { + fm_debug_assert((void*)a._ptr != (void*)-1); + fm_debug_assert((void*)b._ptr != (void*)-1); + } + return a._ptr == b._ptr; +} + +fm_template constexpr std::strong_ordering operator<=>(const fm_basic_iptr& a, const fm_basic_iptr& b) noexcept +{ + return a._ptr <=> b._ptr; +} + +#undef fm_template +#undef fm_basic_iptr + +} // namespace floormat diff --git a/compat/intrusive-ptr.inl b/compat/intrusive-ptr.inl new file mode 100644 index 00000000..b8d40e3b --- /dev/null +++ b/compat/intrusive-ptr.inl @@ -0,0 +1,8 @@ +#pragma once +#include "intrusive-ptr.hpp" + +namespace floormat { + + + +} // namespace floormat -- cgit v1.2.3