#pragma once #include "tl/detail/prologue.h" #include #include "tl/functional.h" #include "tl/atomic.h" #include "tl/assert.h" #include "tl/crash.h" namespace tl { #ifdef TL_DEBUG # define CHECKED_PTR 1 #else # define CHECKED_PTR 0 #endif template class unique_ref; template class unique_ptr; template class lent_ref; template class lent_ptr; template using delete_callback = eastl::function; template struct ptr_traits_passes_virtual_destructor_check { static constexpr inline bool value = !std::is_polymorphic::value || std::is_final::value || std::has_virtual_destructor::value; }; template struct ptr_traits_passes_virtual_destructor_check // check not required { static constexpr inline bool value = true; }; template struct ptr_traits { static constexpr inline bool custom_deleter = false; static constexpr inline bool requires_virtual_destructor_check = true; }; namespace detail { template struct deleter {}; template struct deleter { constexpr deleter() noexcept = default; constexpr deleter(const deleter& other) noexcept = default; constexpr deleter(deleter&& other) noexcept = default; constexpr deleter& operator=(const deleter& other) noexcept = default; constexpr deleter& operator=(deleter&& other) noexcept = default; constexpr void destroy(T* p) noexcept { delete p; } }; template struct deleter { constexpr deleter() noexcept = default; constexpr deleter(delete_callback callback) noexcept : callback(std::move(callback)) {} constexpr deleter(const deleter& other) noexcept = default; constexpr deleter(deleter&& other) noexcept = default; constexpr deleter& operator=(const deleter& other) noexcept = default; constexpr deleter& operator=(deleter&& other) noexcept = default; constexpr void destroy(T* p) noexcept { if (callback && p) callback(p); } delete_callback callback; }; struct ptr_control_block { tl::atomic unique_count = { 1 }; tl::atomic lent_count = { 0 }; }; #if CHECKED_PTR == 1 template Ptr build_copy(T* t, const SrcPtr& src) noexcept { return Ptr(t, src.get_control_block(), Ptr::increment_ref_count); } template Ptr build_move(T* t, SrcPtr& src) noexcept { return Ptr(t, src.release_control_block()); } template Ptr build_move_with_deleter(T* t, SrcPtr& src) noexcept { return Ptr(t, src.get_deleter(), src.release_control_block()); } #else template Ptr build_copy(T* t, const SrcPtr& src) noexcept { return Ptr(t); } template Ptr build_move(T* t, SrcPtr& src) noexcept { return Ptr(t); } template Ptr build_move_with_deleter(T* t, SrcPtr& src) noexcept { return Ptr(t, src.get_deleter()); } #endif extern thread_local int32_t s_ptr_checking_disabled; } #define CHECK_NULL if (!m_ptr) [[unlikely]] TL_PLAIN_CRASH("nullptr") #if CHECKED_PTR == 1 #define CB_DECLARE_NULL() detail::ptr_control_block* m_cb = nullptr #define CB_ALLOCATE() m_cb = new detail::ptr_control_block() #define CB_ASSIGN(other) m_cb = other.get_control_block() #define CB_MOVE(other) m_cb = other.release_control_block() #define CB_LENT_INC_REF() if (m_cb) [[likely]] m_cb->lent_count.fetch_add(1, tl::memory_order_release); #define CB_LENT_DEC_REF() \ if (m_cb) [[likely]] \ { \ const bool is_dangling = m_cb->unique_count.load(tl::memory_order_acquire) == 0; \ if (is_dangling && is_ptr_checking_enabled()) [[unlikely]] \ TL_FAIL("lent: {} dangling lent ptrs detected", m_cb->lent_count.load(tl::memory_order_acquire)); \ if (m_cb->lent_count.fetch_sub(1, tl::memory_order_acquire) == 1) /* last one? cleanup */ \ { \ if (is_dangling) [[unlikely]] \ delete m_cb; \ } \ m_cb = nullptr; \ } #define CB_LENT_COPY_CTOR() \ CB_ASSIGN(other); \ CB_LENT_INC_REF() #define CB_LENT_MOVE_CTOR() \ CB_MOVE(other) #define CB_LENT_COPY_ASSIGN() \ CB_LENT_DEC_REF(); \ CB_ASSIGN(other); \ CB_LENT_INC_REF() #define CB_LENT_MOVE_ASSIGN() \ CB_LENT_DEC_REF(); \ CB_MOVE(other); #define CB_UNIQUE_DEC_REF() \ if (m_cb && m_cb->unique_count.fetch_sub(1, tl::memory_order_acquire) == 1) [[likely]] \ { \ const int32_t count = m_cb->lent_count.load(tl::memory_order_acquire); \ if (count == 0) [[likely]] \ delete m_cb; \ else if (is_ptr_checking_enabled()) [[likely]] \ TL_FAIL("unique: {} dangling lent ptrs detected", count); \ m_cb = nullptr; \ } #define CB_UNIQUE_MOVE_CTOR() \ CB_MOVE(other) #define CB_UNIQUE_MOVE_ASSIGN() \ CB_UNIQUE_DEC_REF(); \ CB_MOVE(other); #else #define CB_DECLARE_NULL() #define CB_ALLOCATE() #define CB_ASSIGN(other) #define CB_MOVE(other) #define CB_LENT_INC_REF() #define CB_LENT_DEC_REF() #define CB_LENT_COPY_CTOR() #define CB_LENT_MOVE_CTOR() #define CB_LENT_COPY_ASSIGN() #define CB_LENT_MOVE_ASSIGN() #define CB_UNIQUE_DEC_REF() #define CB_UNIQUE_MOVE_CTOR() #define CB_UNIQUE_MOVE_ASSIGN() #endif extern void enable_ptr_checking() noexcept; extern void disable_ptr_checking() noexcept; extern void set_ptr_checking_enabled(bool v) noexcept; inline bool is_ptr_checking_enabled() noexcept { return detail::s_ptr_checking_disabled == 0; } struct disable_ptr_checking_scope { disable_ptr_checking_scope() noexcept { disable_ptr_checking(); } ~disable_ptr_checking_scope() noexcept { enable_ptr_checking(); } }; template class unique_ptr { template friend class unique_ref; template friend class unique_ptr; template friend class lent_ref; template friend class lent_ptr; template friend Ptr detail::build_copy(U* t, const SrcPtr& src) noexcept; template friend Ptr detail::build_move(U* t, SrcPtr& src) noexcept; template friend Ptr detail::build_move_with_deleter(U* t, SrcPtr& src) noexcept; using deleter_t = detail::deleter::custom_deleter>; private: #if CHECKED_PTR == 1 using cb_t = detail::ptr_control_block; unique_ptr(T* ptr, cb_t* cb) noexcept requires(ptr_traits::custom_deleter == false) : m_ptr(ptr) , m_cb(cb) { } unique_ptr(T* ptr, delete_callback del, cb_t* cb) noexcept requires(ptr_traits::custom_deleter == true) : m_ptr(ptr) , m_del(std::move(del)) , m_cb(cb) { } cb_t* get_control_block() const noexcept { return m_cb; } cb_t* release_control_block() noexcept { const auto cb = m_cb; m_cb = nullptr; return cb; } #endif public: unique_ptr() = default; explicit unique_ptr(T* ptr) noexcept requires(ptr_traits::custom_deleter == false) : m_ptr(ptr) { CB_ALLOCATE(); } unique_ptr(T* ptr, delete_callback del) noexcept requires(ptr_traits::custom_deleter == true) : m_ptr(ptr) , m_del(std::move(del)) { CB_ALLOCATE(); } unique_ptr(std::nullptr_t) noexcept : m_ptr(nullptr) { CB_ALLOCATE(); } ~unique_ptr() noexcept { m_del.destroy(m_ptr); CB_UNIQUE_DEC_REF(); static_assert(ptr_traits_passes_virtual_destructor_check::requires_virtual_destructor_check>::value, "Destructor is not virtual"); } unique_ptr(const unique_ptr&) noexcept = delete; template unique_ptr(unique_ptr&& other) noexcept : m_ptr(other.release()) { CB_UNIQUE_MOVE_CTOR(); if constexpr (ptr_traits::custom_deleter == true) m_del = std::move(other.m_del); } template unique_ptr(unique_ref&& other) noexcept : m_ptr(other.release()) { CB_UNIQUE_MOVE_CTOR(); if constexpr (ptr_traits::custom_deleter == true) m_del = std::move(other.m_del); } unique_ptr& operator=(const unique_ptr&) noexcept = delete; template unique_ptr& operator=(unique_ptr&& other) noexcept { m_del.destroy(m_ptr); CB_UNIQUE_MOVE_ASSIGN(); m_ptr = other.release(); if constexpr (ptr_traits::custom_deleter == true) m_del = std::move(other.m_del); return *this; } template unique_ptr& operator=(unique_ref&& other) noexcept { m_del.destroy(m_ptr); CB_UNIQUE_MOVE_ASSIGN(); m_ptr = other.release(); if constexpr (ptr_traits::custom_deleter == true) m_del = std::move(other.m_del); return *this; } unique_ptr& operator=(std::nullptr_t) noexcept { reset(nullptr); return *this; } explicit operator bool() const noexcept { return m_ptr != nullptr; } constexpr T* get() const noexcept { return m_ptr; } delete_callback get_deleter() const noexcept requires(ptr_traits::custom_deleter == true) { return m_del.callback; } void reset(T* new_ptr = nullptr) noexcept { auto* p = m_ptr; m_ptr = new_ptr; m_del.destroy(p); CB_UNIQUE_DEC_REF(); CB_ALLOCATE(); } void reset(T* new_ptr, delete_callback del) noexcept requires(ptr_traits::custom_deleter == true) { auto* p = m_ptr; m_ptr = new_ptr; m_del.destroy(p); m_del = std::move(del); CB_UNIQUE_DEC_REF(); CB_ALLOCATE(); } T* release() noexcept { auto* p = m_ptr; m_ptr = nullptr; return p; } T& operator*() const noexcept { CHECK_NULL; return *m_ptr; } T* operator->() const noexcept { CHECK_NULL; return m_ptr; } private: T* m_ptr = nullptr; #ifdef TL_TOOLCHAIN_MSC [[msvc::no_unique_address]] deleter_t m_del; #else [[no_unique_address]] deleter_t m_del; #endif CB_DECLARE_NULL(); }; template class unique_ref { template friend class unique_ref; template friend class unique_ptr; template friend class lent_ref; template friend class lent_ptr; template friend Ptr detail::build_copy(U* t, const SrcPtr& src) noexcept; template friend Ptr detail::build_move(U* t, SrcPtr& src) noexcept; template friend Ptr detail::build_move_with_deleter(U* t, SrcPtr& src) noexcept; using deleter_t = detail::deleter::custom_deleter>; #if CHECKED_PTR == 1 using cb_t = detail::ptr_control_block; unique_ref(T* ptr, cb_t* cb) noexcept requires(ptr_traits::custom_deleter == false) : m_ptr(ptr) , m_cb(cb) { CHECK_NULL; } unique_ref(T* ptr, delete_callback del, cb_t* cb) noexcept requires(ptr_traits::custom_deleter == true) : m_ptr(ptr) , m_del(std::move(del)) , m_cb(cb) { CHECK_NULL; } cb_t* get_control_block() const noexcept { return m_cb; } cb_t* release_control_block() noexcept { const auto cb = m_cb; m_cb = nullptr; return cb; } #endif public: explicit unique_ref(T* ptr) noexcept requires(ptr_traits::custom_deleter == false) : m_ptr(ptr) { CHECK_NULL; CB_ALLOCATE(); } unique_ref(T* ptr, delete_callback del) noexcept requires(ptr_traits::custom_deleter == true) : m_ptr(ptr) , m_del(std::move(del)) { CHECK_NULL; CB_ALLOCATE(); } ~unique_ref() noexcept { static_assert(ptr_traits_passes_virtual_destructor_check::requires_virtual_destructor_check>::value, "Destructor is not virtual"); m_del.destroy(m_ptr); CB_UNIQUE_DEC_REF(); } unique_ref(const unique_ref&) noexcept = delete; template unique_ref(unique_ref&& other) noexcept : m_ptr(other.release()) { CB_UNIQUE_MOVE_CTOR(); if constexpr (ptr_traits::custom_deleter == true) m_del = std::move(other.m_del); } template unique_ref(unique_ptr&& other) noexcept : m_ptr(other.release()) { CB_UNIQUE_MOVE_CTOR(); if constexpr (ptr_traits::custom_deleter == true) m_del = std::move(other.m_del); CHECK_NULL; } unique_ref& operator=(const unique_ref&) noexcept = delete; template unique_ref& operator=(unique_ref&& other) noexcept { m_del.destroy(m_ptr); CB_UNIQUE_MOVE_ASSIGN(); m_ptr = other.release(); if constexpr (ptr_traits::custom_deleter == true) m_del = std::move(other.m_del); CHECK_NULL; CB_MOVE(other); return *this; } void reset(T* new_ptr) noexcept requires(ptr_traits::custom_deleter == false) { auto* p = m_ptr; m_ptr = new_ptr; m_del.destroy(p); CB_UNIQUE_DEC_REF(); CHECK_NULL; CB_ALLOCATE(); } void reset(T* new_ptr, delete_callback del) noexcept requires(ptr_traits::custom_deleter == true) { auto* p = m_ptr; m_ptr = new_ptr; m_del.destroy(p); m_del = std::move(del); CB_UNIQUE_DEC_REF(); CHECK_NULL; CB_ALLOCATE(); } constexpr T* get() const noexcept { return m_ptr; } delete_callback get_deleter() const noexcept requires(ptr_traits::custom_deleter == true) { return m_del.callback; } T* release() noexcept { auto* p = m_ptr; m_ptr = nullptr; return p; } T& operator*() const noexcept { CHECK_NULL; return *m_ptr; } T* operator->() const noexcept { CHECK_NULL; return m_ptr; } private: T* m_ptr = nullptr; #ifdef TL_TOOLCHAIN_MSC [[msvc::no_unique_address]] deleter_t m_del; #else [[no_unique_address]] deleter_t m_del; #endif CB_DECLARE_NULL(); }; template class lent_ptr { template friend class unique_ref; template friend class unique_ptr; template friend class lent_ref; template friend class lent_ptr; template friend Ptr detail::build_copy(U* t, const SrcPtr& src) noexcept; template friend Ptr detail::build_move(U* t, SrcPtr& src) noexcept; template friend Ptr detail::build_move_with_deleter(U* t, SrcPtr& src) noexcept; #if CHECKED_PTR == 1 using cb_t = detail::ptr_control_block; enum increment_ref_count_t { increment_ref_count }; lent_ptr(T* ptr, cb_t* cb, increment_ref_count_t) noexcept : m_ptr(ptr) , m_cb(cb) { CB_LENT_INC_REF(); } lent_ptr(T* ptr, cb_t* cb) noexcept : m_ptr(ptr) , m_cb(cb) { } cb_t* get_control_block() const noexcept { return m_cb; } cb_t* release_control_block() noexcept { const auto cb = m_cb; m_cb = nullptr; return cb; } #else explicit lent_ptr(T* ptr) noexcept : m_ptr(ptr) {} #endif public: lent_ptr() noexcept = default; lent_ptr(std::nullptr_t) noexcept : m_ptr(nullptr) {} ~lent_ptr() noexcept { CB_LENT_DEC_REF(); } lent_ptr(const lent_ptr& other) noexcept : m_ptr(other.m_ptr) { CB_LENT_COPY_CTOR(); } template lent_ptr(const lent_ptr& other) noexcept : m_ptr(other.m_ptr) { CB_LENT_COPY_CTOR(); } template lent_ptr(const lent_ref& other) noexcept : m_ptr(other.m_ptr) { CB_LENT_COPY_CTOR(); } template lent_ptr(const unique_ptr& other) noexcept : m_ptr(other.m_ptr) { CB_LENT_COPY_CTOR(); } template lent_ptr(const unique_ref& other) noexcept : m_ptr(other.m_ptr) { CB_LENT_COPY_CTOR(); } template lent_ptr(unique_ref&& other) = delete; template lent_ptr(unique_ptr&& other) = delete; template lent_ptr& operator = (unique_ref&& other) = delete; template lent_ptr& operator = (unique_ptr&& other) = delete; template lent_ptr(lent_ptr&& other) noexcept : m_ptr(other.m_ptr) { other.m_ptr = nullptr; CB_LENT_MOVE_CTOR(); } template lent_ptr(lent_ref&& other) noexcept : m_ptr(other.m_ptr) { other.m_ptr = nullptr; CB_LENT_MOVE_CTOR(); } lent_ptr& operator=(const lent_ptr& other) noexcept { if (this == &other) [[unlikely]] return *this; CB_LENT_COPY_ASSIGN(); m_ptr = other.m_ptr; return *this; } template lent_ptr& operator=(const lent_ptr& other) noexcept { if (this == &other) [[unlikely]] return *this; CB_LENT_COPY_ASSIGN(); m_ptr = other.m_ptr; return *this; } template lent_ptr& operator=(const lent_ref& other) noexcept { CB_LENT_COPY_ASSIGN(); m_ptr = other.m_ptr; return *this; } template lent_ptr& operator=(const unique_ptr& other) noexcept { CB_LENT_COPY_ASSIGN(); m_ptr = other.m_ptr; return *this; } template lent_ptr& operator=(const unique_ref& other) noexcept { CB_LENT_COPY_ASSIGN(); m_ptr = other.m_ptr; return *this; } lent_ptr& operator=(lent_ptr&& other) noexcept { if (this == &other) [[unlikely]] return *this; CB_LENT_MOVE_ASSIGN(); m_ptr = other.m_ptr; other.m_ptr = nullptr; return *this; } template lent_ptr& operator=(lent_ptr&& other) noexcept { CB_LENT_MOVE_ASSIGN(); m_ptr = other.m_ptr; other.m_ptr = nullptr; return *this; } template lent_ptr& operator=(lent_ref&& other) noexcept { CB_LENT_MOVE_ASSIGN(); m_ptr = other.m_ptr; other.m_ptr = nullptr; return *this; } lent_ptr& operator=(std::nullptr_t) noexcept { CB_LENT_DEC_REF(); m_ptr = nullptr; return *this; } explicit operator bool() const noexcept { return m_ptr != nullptr; } constexpr T* get() const noexcept { return m_ptr; } T& operator*() const noexcept { CHECK_NULL; return *m_ptr; } T* operator->() const noexcept { CHECK_NULL; return m_ptr; } private: T* m_ptr = nullptr; CB_DECLARE_NULL(); }; template class lent_ref { template friend class unique_ref; template friend class unique_ptr; template friend class lent_ref; template friend class lent_ptr; template friend Ptr detail::build_copy(U* t, const SrcPtr& src) noexcept; template friend Ptr detail::build_move(U* t, SrcPtr& src) noexcept; template friend Ptr detail::build_move_with_deleter(U* t, SrcPtr& src) noexcept; #if CHECKED_PTR == 1 using cb_t = detail::ptr_control_block; enum increment_ref_count_t { increment_ref_count }; lent_ref(T* ptr, cb_t* cb, increment_ref_count_t) noexcept : m_ptr(ptr) , m_cb(cb) { CHECK_NULL; CB_LENT_INC_REF(); } lent_ref(T* ptr, cb_t* cb) noexcept : m_ptr(ptr) , m_cb(cb) { CHECK_NULL; } cb_t* get_control_block() const noexcept { return m_cb; } cb_t* release_control_block() noexcept { const auto cb = m_cb; m_cb = nullptr; return cb; } #else explicit lent_ref(T* ptr) noexcept : m_ptr(ptr) { CHECK_NULL; } #endif public: ~lent_ref() noexcept { CB_LENT_DEC_REF(); } lent_ref(const lent_ref& other) noexcept : m_ptr(other.m_ptr) { CHECK_NULL; CB_LENT_COPY_CTOR(); } template lent_ref(const lent_ref& other) noexcept : m_ptr(other.m_ptr) { CHECK_NULL; CB_LENT_COPY_CTOR(); } template lent_ref(const unique_ref& other) noexcept : m_ptr(other.m_ptr) { CHECK_NULL; CB_LENT_COPY_CTOR(); } template lent_ref(unique_ref&& other) = delete; template lent_ref& operator = (unique_ref&& other) = delete; lent_ref(lent_ref&& other) noexcept : m_ptr(other.m_ptr) { CHECK_NULL; other.m_ptr = nullptr; CB_LENT_MOVE_CTOR(); } lent_ref& operator=(const lent_ref& other) noexcept { if (this == &other) [[unlikely]] return *this; CB_LENT_COPY_ASSIGN(); m_ptr = other.m_ptr; CHECK_NULL; return *this; } template lent_ref& operator=(const lent_ref& other) noexcept { if (this == &other) [[unlikely]] return *this; CB_LENT_COPY_ASSIGN(); m_ptr = other.m_ptr; CHECK_NULL; return *this; } template lent_ref& operator=(lent_ref&& other) noexcept { if (this == &other) [[unlikely]] return *this; CB_LENT_MOVE_ASSIGN(); m_ptr = other.m_ptr; CHECK_NULL; other.m_ptr = nullptr; return *this; } constexpr T* get() const noexcept { CHECK_NULL; return m_ptr; } T& operator*() const noexcept { CHECK_NULL; return *m_ptr; } T* operator->() const noexcept { CHECK_NULL; return m_ptr; } private: T* m_ptr = nullptr; CB_DECLARE_NULL(); }; template class lendable { template friend class unique_ref; template friend class unique_ptr; template friend class lent_ref; template friend class lent_ptr; template friend Ptr detail::build_copy(U* t, const SrcPtr& src) noexcept; template friend Ptr detail::build_move(U* t, SrcPtr& src) noexcept; template friend Ptr detail::build_move_with_deleter(U* t, SrcPtr& src) noexcept; #if CHECKED_PTR == 1 using cb_t = detail::ptr_control_block; cb_t* get_control_block() const noexcept { return m_cb; } cb_t* release_control_block() noexcept { const auto cb = m_cb; m_cb = nullptr; return cb; } #endif CB_DECLARE_NULL(); public: template lendable(Args&&... args) noexcept : data(std::forward(args)...) { CB_ALLOCATE(); } explicit lendable(const T& other) noexcept : data(other) { CB_ALLOCATE(); } explicit lendable(T&& other) noexcept : data(std::move(other)) { CB_ALLOCATE(); } ~lendable() noexcept = default; template lendable(const lendable& other) noexcept : data(other.data) { CB_ALLOCATE(); } template lendable& operator=(const lendable& other) noexcept { data = other.data; return *this; } template lendable& operator=(lendable&& other) noexcept { data = std::move(other.data); return *this; } lendable& operator=(const lendable& other) noexcept { if (this == &other) [[unlikely]] return *this; data = other.data; return *this; } lendable& operator=(lendable&& other) noexcept { data = std::move(other.data); return *this; } constexpr const T& get() const noexcept { return data; } constexpr T& get() noexcept { return data; } T& operator*() noexcept { return data; } const T& operator*() const noexcept { return data; } const T* operator->() const noexcept { return &data; } T* operator->() noexcept { return &data; } private: T data; }; // unique - unique template constexpr auto operator<=>(const unique_ref& a, const unique_ref& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const unique_ref& a, const unique_ref& b) noexcept { return a.get() == b.get(); } template constexpr auto operator<=>(const unique_ptr& a, const unique_ptr& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const unique_ptr& a, const unique_ptr& b) noexcept { return a.get() == b.get(); } template constexpr auto operator<=>(const unique_ptr& a, const unique_ref& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const unique_ptr& a, const unique_ref& b) noexcept { return a.get() == b.get(); } template constexpr auto operator<=>(const unique_ref& a, const unique_ptr& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const unique_ref& a, const unique_ptr& b) noexcept { return a.get() == b.get(); } //unique - lent template constexpr auto operator<=>(const unique_ref& a, const lent_ref& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const unique_ref& a, const lent_ref& b) noexcept { return a.get() == b.get(); } template constexpr auto operator<=>(const unique_ptr& a, const lent_ptr& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const unique_ptr& a, const lent_ptr& b) noexcept { return a.get() == b.get(); } template constexpr auto operator<=>(const unique_ptr& a, const lent_ref& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const unique_ptr& a, const lent_ref& b) noexcept { return a.get() == b.get(); } template constexpr auto operator<=>(const unique_ref& a, const lent_ptr& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const unique_ref& a, const lent_ptr& b) noexcept { return a.get() == b.get(); } //lendable - lent template constexpr auto operator<=>(const lendable& a, const lent_ref& b) noexcept { return &a.get() <=> b.get(); } template constexpr bool operator== (const lendable& a, const lent_ref& b) noexcept { return &a.get() == b.get(); } template constexpr auto operator<=>(const lendable& a, const lent_ptr& b) noexcept { return &a.get() <=> b.get(); } template constexpr bool operator== (const lendable& a, const lent_ptr& b) noexcept { return &a.get() == b.get(); } //unique - raw template constexpr auto operator<=>(const unique_ref& a, const U* b) noexcept { return a.get() <=> b; } template constexpr bool operator== (const unique_ref& a, const U* b) noexcept { return a.get() == b; } template constexpr auto operator<=>(const unique_ptr& a, const U* b) noexcept { return a.get() <=> b; } template constexpr bool operator== (const unique_ptr& a, const U* b) noexcept { return a.get() == b; } //unique - nullptr template constexpr auto operator<=>(const unique_ref& a, std::nullptr_t) noexcept { return a.get() <=> nullptr; } template constexpr bool operator== (const unique_ref& a, std::nullptr_t) noexcept { return a.get() == nullptr; } template constexpr auto operator<=>(const unique_ptr& a, std::nullptr_t) noexcept { return a.get() <=> nullptr; } template constexpr bool operator== (const unique_ptr& a, std::nullptr_t) noexcept { return a.get() == nullptr; } //lent - unique template constexpr auto operator<=>(const lent_ref& a, const unique_ref& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const lent_ref& a, const unique_ref& b) noexcept { return a.get() == b.get(); } template constexpr auto operator<=>(const lent_ptr& a, const unique_ptr& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const lent_ptr& a, const unique_ptr& b) noexcept { return a.get() == b.get(); } template constexpr auto operator<=>(const lent_ptr& a, const unique_ref& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const lent_ptr& a, const unique_ref& b) noexcept { return a.get() == b.get(); } template constexpr auto operator<=>(const lent_ref& a, const unique_ptr& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const lent_ref& a, const unique_ptr& b) noexcept { return a.get() == b.get(); } //raw - unique template constexpr auto operator<=>(const T* a, const unique_ref& b) noexcept { return a <=> b.get(); } template constexpr bool operator== (const T* a, const unique_ref& b) noexcept { return a == b.get(); } template constexpr auto operator<=>(const T* a, const unique_ptr& b) noexcept { return a <=> b.get(); } template constexpr bool operator== (const T* a, const unique_ptr& b) noexcept { return a == b.get(); } //nullptr - unique template constexpr auto operator<=>(std::nullptr_t, const unique_ref& b) noexcept { return nullptr <=> b.get(); } template constexpr bool operator== (std::nullptr_t, const unique_ref& b) noexcept { return nullptr == b.get(); } template constexpr auto operator<=>(std::nullptr_t, const unique_ptr& b) noexcept { return nullptr <=> b.get(); } template constexpr bool operator== (std::nullptr_t, const unique_ptr& b) noexcept { return nullptr == b.get(); } //lent - lent template constexpr auto operator<=>(const lent_ref& a, const lent_ref& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const lent_ref& a, const lent_ref& b) noexcept { return a.get() == b.get(); } template constexpr auto operator<=>(const lent_ptr& a, const lent_ptr& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const lent_ptr& a, const lent_ptr& b) noexcept { return a.get() == b.get(); } template constexpr auto operator<=>(const lent_ptr& a, const lent_ref& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const lent_ptr& a, const lent_ref& b) noexcept { return a.get() == b.get(); } template constexpr auto operator<=>(const lent_ref& a, const lent_ptr& b) noexcept { return a.get() <=> b.get(); } template constexpr bool operator== (const lent_ref& a, const lent_ptr& b) noexcept { return a.get() == b.get(); } //lent - raw template constexpr auto operator<=>(const lent_ref& a, const U* b) noexcept { return a.get() <=> b; } template constexpr bool operator== (const lent_ref& a, const U* b) noexcept { return a.get() == b; } template constexpr auto operator<=>(const lent_ptr& a, const U* b) noexcept { return a.get() <=> b; } template constexpr bool operator== (const lent_ptr& a, const U* b) noexcept { return a.get() == b; } //lent - nullptr template constexpr auto operator<=>(const lent_ref& a, std::nullptr_t) noexcept { return a.get() <=> nullptr; } template constexpr bool operator== (const lent_ref& a, std::nullptr_t) noexcept { return a.get() == nullptr; } template constexpr auto operator<=>(const lent_ptr& a, std::nullptr_t) noexcept { return a.get() <=> nullptr; } template constexpr bool operator== (const lent_ptr& a, std::nullptr_t) noexcept { return a.get() == nullptr; } //raw - lent template constexpr auto operator<=>(const T* a, const lent_ref& b) noexcept { return a <=> b.get(); } template constexpr bool operator== (const T* a, const lent_ref& b) noexcept { return a == b.get(); } template constexpr auto operator<=>(const T* a, const lent_ptr& b) noexcept { return a <=> b.get(); } template constexpr bool operator== (const T* a, const lent_ptr& b) noexcept { return a == b.get(); } //raw - lent template constexpr auto operator<=>(std::nullptr_t, const lent_ref& b) noexcept { return nullptr <=> b.get(); } template constexpr bool operator== (std::nullptr_t, const lent_ref& b) noexcept { return nullptr == b.get(); } template constexpr auto operator<=>(std::nullptr_t, const lent_ptr& b) noexcept { return nullptr <=> b.get(); } template constexpr bool operator== (std::nullptr_t, const lent_ptr& b) noexcept { return nullptr == b.get(); } template class lendable_base { template friend Ptr detail::build_copy(U* t, const SrcPtr& src) noexcept; template friend Ptr detail::build_move(U* t, SrcPtr& src) noexcept; template friend Ptr detail::build_move_with_deleter(U* t, SrcPtr& src) noexcept; private: #if CHECKED_PTR == 1 using cb_t = detail::ptr_control_block; cb_t* get_control_block() const noexcept { return m_cb; } cb_t* release_control_block() noexcept { const auto cb = m_cb; m_cb = nullptr; return cb; } CB_DECLARE_NULL(); protected: ~lendable_base() noexcept { CB_UNIQUE_DEC_REF(); } public: lendable_base() noexcept { CB_ALLOCATE(); } lendable_base(const lendable_base&) noexcept { CB_ALLOCATE(); } lendable_base(lendable_base&&) noexcept { CB_ALLOCATE(); } lendable_base& operator=(const lendable_base&) noexcept { return *this; } lendable_base& operator=(lendable_base&&) noexcept { return *this; } #else protected: ~lendable_base() noexcept = default; public: lendable_base() noexcept = default; lendable_base(const lendable_base&) noexcept = default; lendable_base(lendable_base&&) noexcept = default; lendable_base& operator=(const lendable_base&) noexcept = default; lendable_base& operator=(lendable_base&&) noexcept = default; #endif }; template unique_ref make_unique(Args&&... args) noexcept { return unique_ref(new T(std::forward(args)...)); } template unique_ref make_unique_del(T* p, Deleter del) noexcept { return unique_ref(p, std::move(del)); } template lent_ref promote(lent_ptr ptr) noexcept { return detail::build_move>(ptr.get(), ptr); } template unique_ref promote(unique_ptr&& ptr) noexcept { if constexpr (ptr_traits::custom_deleter) return detail::build_move_with_deleter>(ptr.release(), ptr); else return detail::build_move>(ptr.release(), ptr); } template unique_ptr demote(unique_ref ptr) noexcept { if constexpr (ptr_traits::custom_deleter) return detail::build_move_with_deleter>(ptr.release(), ptr); else return detail::build_move>(ptr.release(), ptr); } template lent_ptr demote(lent_ref ptr) noexcept { return detail::build_move>(ptr.get(), ptr); } template lent_ptr demote(const lendable& ptr) noexcept { return detail::build_copy>((T*)&ptr.get(), ptr); } template lent_ptr demote(const lendable_base& ptr) noexcept { return detail::build_copy>((T*)&ptr, ptr); } template lent_ptr lend(const unique_ptr& ptr) noexcept { return detail::build_copy>(ptr.get(), ptr); } template lent_ptr lend(const unique_ptr& ptr) noexcept { return detail::build_copy>(const_cast(static_cast(ptr.get())), ptr); } template lent_ref lend(const unique_ref& ptr) noexcept { return detail::build_copy>(ptr.get(), ptr); } template lent_ref lend(const unique_ref& ptr) noexcept { return detail::build_copy>(const_cast(static_cast(ptr.get())), ptr); } template lent_ref lend(const lendable_base& ptr) noexcept { return detail::build_copy>(const_cast(static_cast(&ptr)), ptr); } template lent_ref lend(const lendable_base* ptr) noexcept { return detail::build_copy>(const_cast(static_cast(ptr)), *ptr); } template lent_ref lend(const lendable_base& ptr) noexcept { return detail::build_copy>((T*)&ptr, ptr); } template lent_ref lend(const lendable_base* ptr) noexcept { return detail::build_copy>((T*)ptr, *ptr); } template lent_ref lend(const lendable& ptr) noexcept { return detail::build_copy>((T*)&ptr.get(), ptr); } template lent_ref lend(const T* ptr) noexcept { return detail::build_copy>(const_cast(static_cast(ptr)), *ptr); } template lent_ref promote(const unique_ptr& ptr) noexcept { return detail::build_copy>(ptr.get(), ptr); } template lent_ptr dynamic_lent_cast(lent_ptr ptr) noexcept { return detail::build_move>(dynamic_cast(ptr.get()), ptr); } template lent_ptr dynamic_lent_cast(lent_ref ptr) noexcept { return detail::build_move>(dynamic_cast(ptr.get()), ptr); } template lent_ptr dynamic_lent_cast(const unique_ptr& ptr) noexcept { return detail::build_copy>(dynamic_cast(ptr.get()), ptr); } template lent_ptr dynamic_lent_cast(const unique_ref& ptr) noexcept { return detail::build_copy>(dynamic_cast(ptr.get()), ptr); } template lent_ptr static_lent_cast(lent_ptr ptr) noexcept { return detail::build_move>(static_cast(ptr.get()), ptr); } template lent_ref static_lent_cast(lent_ref ptr) noexcept { return detail::build_move>(static_cast(ptr.get()), ptr); } template lent_ptr static_lent_cast(const unique_ptr& ptr) noexcept { return detail::build_copy>(static_cast(ptr.get()), ptr); } template lent_ref static_lent_cast(const unique_ref& ptr) noexcept { return detail::build_copy>(static_cast(ptr.get()), ptr); } template unique_ptr dynamic_unique_cast(unique_ptr ptr) noexcept { auto* p = ptr.release(); if (auto* t = dynamic_cast(p)) { if constexpr (ptr_traits::custom_deleter) return detail::build_move_with_deleter>(t, ptr); else return detail::build_move>(t, ptr); } delete p; return nullptr; } template unique_ptr dynamic_unique_cast(unique_ref ptr) noexcept { auto* p = ptr.release(); if (auto* t = dynamic_cast(p)) { if constexpr (ptr_traits::custom_deleter) return detail::build_move_with_deleter>(t, ptr); else return detail::build_move>(t, ptr); } delete p; return nullptr; } template lent_ref const_lent_cast(lent_ref ref) noexcept { return detail::build_copy>(const_cast(ref.get()), ref); } template lent_ptr const_lent_cast(lent_ptr ptr) noexcept { return detail::build_copy>(const_cast(ptr.get()), ptr); } } namespace std { template struct hash> { size_t operator()(const tl::lent_ref& ptr) const noexcept { std::hash hasher; return hasher(ptr.get()); } }; template struct hash> { size_t operator()(const tl::lent_ptr& ptr) const noexcept { std::hash hasher; return hasher(ptr.get()); } }; template struct hash> { size_t operator()(const tl::unique_ref& ptr) const noexcept { std::hash hasher; return hasher(ptr.get()); } }; template struct hash> { size_t operator()(const tl::unique_ptr& ptr) const noexcept { std::hash hasher; return hasher(ptr.get()); } }; } namespace eastl { template struct hash> { size_t operator()(const tl::lent_ref& ptr) const noexcept { eastl::hash hasher; return hasher(ptr.get()); } }; template struct hash> { size_t operator()(const tl::lent_ptr& ptr) const noexcept { eastl::hash hasher; return hasher(ptr.get()); } }; template struct hash> { size_t operator()(const tl::unique_ref& ptr) const noexcept { eastl::hash hasher; return hasher(ptr.get()); } }; template struct hash> { size_t operator()(const tl::unique_ptr& ptr) const noexcept { eastl::hash hasher; return hasher(ptr.get()); } }; #undef CHECK_NULL }