Files
TL/include/tl/ptr.h
T
jeanlemotan b307f44333 Small algorithm improvements to avoid allocations
TL depends on cppcoro now
2024-07-03 16:14:16 +02:00

1270 lines
40 KiB
C++

#pragma once
#include "tl/detail/prologue.h"
#include <compare>
#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<typename T> class unique_ref;
template<typename T> class unique_ptr;
template<typename T> class lent_ref;
template<typename T> class lent_ptr;
template<typename T>
using delete_callback = eastl::function<void(T*)>;
template<typename T, bool>
struct ptr_traits_passes_virtual_destructor_check
{
static constexpr inline bool value = !std::is_polymorphic<T>::value || std::is_final<T>::value || std::has_virtual_destructor<T>::value;
};
template<typename T>
struct ptr_traits_passes_virtual_destructor_check<T, false> // check not required
{
static constexpr inline bool value = true;
};
template<typename T>
struct ptr_traits
{
static constexpr inline bool custom_deleter = false;
static constexpr inline bool requires_virtual_destructor_check = true;
};
namespace detail
{
template<typename T, bool Custom>
struct deleter
{};
template<typename T>
struct deleter<T, false>
{
constexpr deleter() noexcept = default;
constexpr deleter(const deleter<T, false>& other) noexcept = default;
constexpr deleter(deleter<T, false>&& other) noexcept = default;
constexpr deleter& operator=(const deleter<T, false>& other) noexcept = default;
constexpr deleter& operator=(deleter<T, false>&& other) noexcept = default;
constexpr void destroy(T* p) noexcept { delete p; }
};
template<typename T>
struct deleter<T, true>
{
constexpr deleter() noexcept = default;
constexpr deleter(delete_callback<T> callback) noexcept : callback(std::move(callback)) {}
constexpr deleter(const deleter<T, true>& other) noexcept = default;
constexpr deleter(deleter<T, true>&& other) noexcept = default;
constexpr deleter& operator=(const deleter<T, true>& other) noexcept = default;
constexpr deleter& operator=(deleter<T, true>&& other) noexcept = default;
constexpr void destroy(T* p) noexcept { if (callback && p) callback(p); }
delete_callback<T> callback;
};
struct ptr_control_block
{
tl::atomic<int32_t> unique_count = { 1 };
tl::atomic<int32_t> lent_count = { 0 };
};
#if CHECKED_PTR == 1
template<typename Ptr, typename SrcPtr, typename T> Ptr build_copy(T* t, const SrcPtr& src) noexcept { return Ptr(t, src.get_control_block(), Ptr::increment_ref_count); }
template<typename Ptr, typename SrcPtr, typename T> Ptr build_move(T* t, SrcPtr& src) noexcept { return Ptr(t, src.release_control_block()); }
template<typename Ptr, typename SrcPtr, typename T> Ptr build_move_with_deleter(T* t, SrcPtr& src) noexcept { return Ptr(t, src.get_deleter(), src.release_control_block()); }
#else
template<typename Ptr, typename SrcPtr, typename T> Ptr build_copy(T* t, const SrcPtr& src) noexcept { return Ptr(t); }
template<typename Ptr, typename SrcPtr, typename T> Ptr build_move(T* t, SrcPtr& src) noexcept { return Ptr(t); }
template<typename Ptr, typename SrcPtr, typename T> 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<typename T>
class unique_ptr
{
template<typename U> friend class unique_ref;
template<typename U> friend class unique_ptr;
template<typename U> friend class lent_ref;
template<typename U> friend class lent_ptr;
template<typename Ptr, typename SrcPtr, typename U> friend Ptr detail::build_copy(U* t, const SrcPtr& src) noexcept;
template<typename Ptr, typename SrcPtr, typename U> friend Ptr detail::build_move(U* t, SrcPtr& src) noexcept;
template<typename Ptr, typename SrcPtr, typename U> friend Ptr detail::build_move_with_deleter(U* t, SrcPtr& src) noexcept;
using deleter_t = detail::deleter<T, ptr_traits<T>::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<T>::custom_deleter == false)
: m_ptr(ptr)
, m_cb(cb)
{
}
unique_ptr(T* ptr, delete_callback<T> del, cb_t* cb) noexcept requires(ptr_traits<T>::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<T>::custom_deleter == false)
: m_ptr(ptr)
{
CB_ALLOCATE();
}
unique_ptr(T* ptr, delete_callback<T> del) noexcept requires(ptr_traits<T>::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<T, ptr_traits<T>::requires_virtual_destructor_check>::value, "Destructor is not virtual");
}
unique_ptr(const unique_ptr<T>&) noexcept = delete;
template<typename U> unique_ptr(unique_ptr<U>&& other) noexcept
: m_ptr(other.release())
{
CB_UNIQUE_MOVE_CTOR();
if constexpr (ptr_traits<T>::custom_deleter == true)
m_del = std::move(other.m_del);
}
template<typename U> unique_ptr(unique_ref<U>&& other) noexcept
: m_ptr(other.release())
{
CB_UNIQUE_MOVE_CTOR();
if constexpr (ptr_traits<T>::custom_deleter == true)
m_del = std::move(other.m_del);
}
unique_ptr& operator=(const unique_ptr<T>&) noexcept = delete;
template<typename U> unique_ptr& operator=(unique_ptr<U>&& other) noexcept
{
m_del.destroy(m_ptr);
CB_UNIQUE_MOVE_ASSIGN();
m_ptr = other.release();
if constexpr (ptr_traits<T>::custom_deleter == true)
m_del = std::move(other.m_del);
return *this;
}
template<typename U> unique_ptr& operator=(unique_ref<U>&& other) noexcept
{
m_del.destroy(m_ptr);
CB_UNIQUE_MOVE_ASSIGN();
m_ptr = other.release();
if constexpr (ptr_traits<T>::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<T> get_deleter() const noexcept requires(ptr_traits<T>::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<T> del) noexcept requires(ptr_traits<T>::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<typename T>
class unique_ref
{
template<typename U> friend class unique_ref;
template<typename U> friend class unique_ptr;
template<typename U> friend class lent_ref;
template<typename U> friend class lent_ptr;
template<typename Ptr, typename SrcPtr, typename U> friend Ptr detail::build_copy(U* t, const SrcPtr& src) noexcept;
template<typename Ptr, typename SrcPtr, typename U> friend Ptr detail::build_move(U* t, SrcPtr& src) noexcept;
template<typename Ptr, typename SrcPtr, typename U> friend Ptr detail::build_move_with_deleter(U* t, SrcPtr& src) noexcept;
using deleter_t = detail::deleter<T, ptr_traits<T>::custom_deleter>;
#if CHECKED_PTR == 1
using cb_t = detail::ptr_control_block;
unique_ref(T* ptr, cb_t* cb) noexcept requires(ptr_traits<T>::custom_deleter == false)
: m_ptr(ptr)
, m_cb(cb)
{
CHECK_NULL;
}
unique_ref(T* ptr, delete_callback<T> del, cb_t* cb) noexcept requires(ptr_traits<T>::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<T>::custom_deleter == false)
: m_ptr(ptr)
{
CHECK_NULL;
CB_ALLOCATE();
}
unique_ref(T* ptr, delete_callback<T> del) noexcept requires(ptr_traits<T>::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<T, ptr_traits<T>::requires_virtual_destructor_check>::value, "Destructor is not virtual");
m_del.destroy(m_ptr);
CB_UNIQUE_DEC_REF();
}
unique_ref(const unique_ref<T>&) noexcept = delete;
template<typename U> unique_ref(unique_ref<U>&& other) noexcept
: m_ptr(other.release())
{
CB_UNIQUE_MOVE_CTOR();
if constexpr (ptr_traits<T>::custom_deleter == true)
m_del = std::move(other.m_del);
}
template<typename U> unique_ref(unique_ptr<U>&& other) noexcept
: m_ptr(other.release())
{
CB_UNIQUE_MOVE_CTOR();
if constexpr (ptr_traits<T>::custom_deleter == true)
m_del = std::move(other.m_del);
CHECK_NULL;
}
unique_ref& operator=(const unique_ref<T>&) noexcept = delete;
template<typename U> unique_ref& operator=(unique_ref<U>&& other) noexcept
{
m_del.destroy(m_ptr);
CB_UNIQUE_MOVE_ASSIGN();
m_ptr = other.release();
if constexpr (ptr_traits<T>::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<T>::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<T> del) noexcept requires(ptr_traits<T>::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<T> get_deleter() const noexcept requires(ptr_traits<T>::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<typename T>
class lent_ptr
{
template<typename U> friend class unique_ref;
template<typename U> friend class unique_ptr;
template<typename U> friend class lent_ref;
template<typename U> friend class lent_ptr;
template<typename Ptr, typename SrcPtr, typename U> friend Ptr detail::build_copy(U* t, const SrcPtr& src) noexcept;
template<typename Ptr, typename SrcPtr, typename U> friend Ptr detail::build_move(U* t, SrcPtr& src) noexcept;
template<typename Ptr, typename SrcPtr, typename U> 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<T>& other) noexcept
: m_ptr(other.m_ptr)
{
CB_LENT_COPY_CTOR();
}
template<typename U> lent_ptr(const lent_ptr<U>& other) noexcept
: m_ptr(other.m_ptr)
{
CB_LENT_COPY_CTOR();
}
template<typename U> lent_ptr(const lent_ref<U>& other) noexcept
: m_ptr(other.m_ptr)
{
CB_LENT_COPY_CTOR();
}
template<typename U> lent_ptr(const unique_ptr<U>& other) noexcept
: m_ptr(other.m_ptr)
{
CB_LENT_COPY_CTOR();
}
template<typename U> lent_ptr(const unique_ref<U>& other) noexcept
: m_ptr(other.m_ptr)
{
CB_LENT_COPY_CTOR();
}
template<typename U> lent_ptr(unique_ref<U>&& other) = delete;
template<typename U> lent_ptr(unique_ptr<U>&& other) = delete;
template<typename U> lent_ptr& operator = (unique_ref<U>&& other) = delete;
template<typename U> lent_ptr& operator = (unique_ptr<U>&& other) = delete;
template<typename U> lent_ptr(lent_ptr<U>&& other) noexcept
: m_ptr(other.m_ptr)
{
other.m_ptr = nullptr;
CB_LENT_MOVE_CTOR();
}
template<typename U> lent_ptr(lent_ref<U>&& other) noexcept
: m_ptr(other.m_ptr)
{
other.m_ptr = nullptr;
CB_LENT_MOVE_CTOR();
}
lent_ptr& operator=(const lent_ptr<T>& other) noexcept
{
if (this == &other) [[unlikely]]
return *this;
CB_LENT_COPY_ASSIGN();
m_ptr = other.m_ptr;
return *this;
}
template<typename U> lent_ptr& operator=(const lent_ptr<U>& other) noexcept
{
if (this == &other) [[unlikely]]
return *this;
CB_LENT_COPY_ASSIGN();
m_ptr = other.m_ptr;
return *this;
}
template<typename U> lent_ptr& operator=(const lent_ref<U>& other) noexcept
{
CB_LENT_COPY_ASSIGN();
m_ptr = other.m_ptr;
return *this;
}
template<typename U> lent_ptr& operator=(const unique_ptr<U>& other) noexcept
{
CB_LENT_COPY_ASSIGN();
m_ptr = other.m_ptr;
return *this;
}
template<typename U> lent_ptr& operator=(const unique_ref<U>& other) noexcept
{
CB_LENT_COPY_ASSIGN();
m_ptr = other.m_ptr;
return *this;
}
lent_ptr& operator=(lent_ptr<T>&& other) noexcept
{
if (this == &other) [[unlikely]]
return *this;
CB_LENT_MOVE_ASSIGN();
m_ptr = other.m_ptr;
other.m_ptr = nullptr;
return *this;
}
template<typename U> lent_ptr& operator=(lent_ptr<U>&& other) noexcept
{
CB_LENT_MOVE_ASSIGN();
m_ptr = other.m_ptr;
other.m_ptr = nullptr;
return *this;
}
template<typename U> lent_ptr& operator=(lent_ref<U>&& 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<typename T>
class lent_ref
{
template<typename U> friend class unique_ref;
template<typename U> friend class unique_ptr;
template<typename U> friend class lent_ref;
template<typename U> friend class lent_ptr;
template<typename Ptr, typename SrcPtr, typename U> friend Ptr detail::build_copy(U* t, const SrcPtr& src) noexcept;
template<typename Ptr, typename SrcPtr, typename U> friend Ptr detail::build_move(U* t, SrcPtr& src) noexcept;
template<typename Ptr, typename SrcPtr, typename U> 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<T>& other) noexcept
: m_ptr(other.m_ptr)
{
CHECK_NULL;
CB_LENT_COPY_CTOR();
}
template<typename U> lent_ref(const lent_ref<U>& other) noexcept
: m_ptr(other.m_ptr)
{
CHECK_NULL;
CB_LENT_COPY_CTOR();
}
template<typename U> lent_ref(const unique_ref<U>& other) noexcept
: m_ptr(other.m_ptr)
{
CHECK_NULL;
CB_LENT_COPY_CTOR();
}
template<typename U> lent_ref(unique_ref<U>&& other) = delete;
template<typename U> lent_ref& operator = (unique_ref<U>&& other) = delete;
lent_ref(lent_ref<T>&& other) noexcept
: m_ptr(other.m_ptr)
{
CHECK_NULL;
other.m_ptr = nullptr;
CB_LENT_MOVE_CTOR();
}
lent_ref& operator=(const lent_ref<T>& other) noexcept
{
if (this == &other) [[unlikely]]
return *this;
CB_LENT_COPY_ASSIGN();
m_ptr = other.m_ptr;
CHECK_NULL;
return *this;
}
template<typename U> lent_ref& operator=(const lent_ref<U>& other) noexcept
{
if (this == &other) [[unlikely]]
return *this;
CB_LENT_COPY_ASSIGN();
m_ptr = other.m_ptr;
CHECK_NULL;
return *this;
}
template<typename U> lent_ref& operator=(lent_ref<U>&& 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<typename T>
class lendable
{
template<typename U> friend class unique_ref;
template<typename U> friend class unique_ptr;
template<typename U> friend class lent_ref;
template<typename U> friend class lent_ptr;
template<typename Ptr, typename SrcPtr, typename U> friend Ptr detail::build_copy(U* t, const SrcPtr& src) noexcept;
template<typename Ptr, typename SrcPtr, typename U> friend Ptr detail::build_move(U* t, SrcPtr& src) noexcept;
template<typename Ptr, typename SrcPtr, typename U> 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<typename... Args>
lendable(Args&&... args) noexcept
: data(std::forward<Args>(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<typename U> lendable(const lendable<U>& other) noexcept
: data(other.data)
{
CB_ALLOCATE();
}
template<typename U> lendable& operator=(const lendable<U>& other) noexcept
{
data = other.data;
return *this;
}
template<typename U> lendable& operator=(lendable<U>&& other) noexcept
{
data = std::move(other.data);
return *this;
}
lendable& operator=(const lendable<T>& other) noexcept
{
if (this == &other) [[unlikely]]
return *this;
data = other.data;
return *this;
}
lendable& operator=(lendable<T>&& 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<typename T, typename U> constexpr auto operator<=>(const unique_ref<T>& a, const unique_ref<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const unique_ref<T>& a, const unique_ref<U>& b) noexcept { return a.get() == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const unique_ptr<T>& a, const unique_ptr<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const unique_ptr<T>& a, const unique_ptr<U>& b) noexcept { return a.get() == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const unique_ptr<T>& a, const unique_ref<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const unique_ptr<T>& a, const unique_ref<U>& b) noexcept { return a.get() == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const unique_ref<T>& a, const unique_ptr<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const unique_ref<T>& a, const unique_ptr<U>& b) noexcept { return a.get() == b.get(); }
//unique - lent
template<typename T, typename U> constexpr auto operator<=>(const unique_ref<T>& a, const lent_ref<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const unique_ref<T>& a, const lent_ref<U>& b) noexcept { return a.get() == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const unique_ptr<T>& a, const lent_ptr<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const unique_ptr<T>& a, const lent_ptr<U>& b) noexcept { return a.get() == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const unique_ptr<T>& a, const lent_ref<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const unique_ptr<T>& a, const lent_ref<U>& b) noexcept { return a.get() == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const unique_ref<T>& a, const lent_ptr<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const unique_ref<T>& a, const lent_ptr<U>& b) noexcept { return a.get() == b.get(); }
//lendable - lent
template<typename T, typename U> constexpr auto operator<=>(const lendable<T>& a, const lent_ref<U>& b) noexcept { return &a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const lendable<T>& a, const lent_ref<U>& b) noexcept { return &a.get() == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const lendable<T>& a, const lent_ptr<U>& b) noexcept { return &a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const lendable<T>& a, const lent_ptr<U>& b) noexcept { return &a.get() == b.get(); }
//unique - raw
template<typename T, typename U> constexpr auto operator<=>(const unique_ref<T>& a, const U* b) noexcept { return a.get() <=> b; }
template<typename T, typename U> constexpr bool operator== (const unique_ref<T>& a, const U* b) noexcept { return a.get() == b; }
template<typename T, typename U> constexpr auto operator<=>(const unique_ptr<T>& a, const U* b) noexcept { return a.get() <=> b; }
template<typename T, typename U> constexpr bool operator== (const unique_ptr<T>& a, const U* b) noexcept { return a.get() == b; }
//unique - nullptr
template<typename T> constexpr auto operator<=>(const unique_ref<T>& a, std::nullptr_t) noexcept { return a.get() <=> nullptr; }
template<typename T> constexpr bool operator== (const unique_ref<T>& a, std::nullptr_t) noexcept { return a.get() == nullptr; }
template<typename T> constexpr auto operator<=>(const unique_ptr<T>& a, std::nullptr_t) noexcept { return a.get() <=> nullptr; }
template<typename T> constexpr bool operator== (const unique_ptr<T>& a, std::nullptr_t) noexcept { return a.get() == nullptr; }
//lent - unique
template<typename T, typename U> constexpr auto operator<=>(const lent_ref<T>& a, const unique_ref<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const lent_ref<T>& a, const unique_ref<U>& b) noexcept { return a.get() == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const lent_ptr<T>& a, const unique_ptr<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const lent_ptr<T>& a, const unique_ptr<U>& b) noexcept { return a.get() == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const lent_ptr<T>& a, const unique_ref<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const lent_ptr<T>& a, const unique_ref<U>& b) noexcept { return a.get() == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const lent_ref<T>& a, const unique_ptr<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const lent_ref<T>& a, const unique_ptr<U>& b) noexcept { return a.get() == b.get(); }
//raw - unique
template<typename T, typename U> constexpr auto operator<=>(const T* a, const unique_ref<U>& b) noexcept { return a <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const T* a, const unique_ref<U>& b) noexcept { return a == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const T* a, const unique_ptr<U>& b) noexcept { return a <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const T* a, const unique_ptr<U>& b) noexcept { return a == b.get(); }
//nullptr - unique
template<typename T> constexpr auto operator<=>(std::nullptr_t, const unique_ref<T>& b) noexcept { return nullptr <=> b.get(); }
template<typename T> constexpr bool operator== (std::nullptr_t, const unique_ref<T>& b) noexcept { return nullptr == b.get(); }
template<typename T> constexpr auto operator<=>(std::nullptr_t, const unique_ptr<T>& b) noexcept { return nullptr <=> b.get(); }
template<typename T> constexpr bool operator== (std::nullptr_t, const unique_ptr<T>& b) noexcept { return nullptr == b.get(); }
//lent - lent
template<typename T, typename U> constexpr auto operator<=>(const lent_ref<T>& a, const lent_ref<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const lent_ref<T>& a, const lent_ref<U>& b) noexcept { return a.get() == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const lent_ptr<T>& a, const lent_ptr<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const lent_ptr<T>& a, const lent_ptr<U>& b) noexcept { return a.get() == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const lent_ptr<T>& a, const lent_ref<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const lent_ptr<T>& a, const lent_ref<U>& b) noexcept { return a.get() == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const lent_ref<T>& a, const lent_ptr<U>& b) noexcept { return a.get() <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const lent_ref<T>& a, const lent_ptr<U>& b) noexcept { return a.get() == b.get(); }
//lent - raw
template<typename T, typename U> constexpr auto operator<=>(const lent_ref<T>& a, const U* b) noexcept { return a.get() <=> b; }
template<typename T, typename U> constexpr bool operator== (const lent_ref<T>& a, const U* b) noexcept { return a.get() == b; }
template<typename T, typename U> constexpr auto operator<=>(const lent_ptr<T>& a, const U* b) noexcept { return a.get() <=> b; }
template<typename T, typename U> constexpr bool operator== (const lent_ptr<T>& a, const U* b) noexcept { return a.get() == b; }
//lent - nullptr
template<typename T> constexpr auto operator<=>(const lent_ref<T>& a, std::nullptr_t) noexcept { return a.get() <=> nullptr; }
template<typename T> constexpr bool operator== (const lent_ref<T>& a, std::nullptr_t) noexcept { return a.get() == nullptr; }
template<typename T> constexpr auto operator<=>(const lent_ptr<T>& a, std::nullptr_t) noexcept { return a.get() <=> nullptr; }
template<typename T> constexpr bool operator== (const lent_ptr<T>& a, std::nullptr_t) noexcept { return a.get() == nullptr; }
//raw - lent
template<typename T, typename U> constexpr auto operator<=>(const T* a, const lent_ref<U>& b) noexcept { return a <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const T* a, const lent_ref<U>& b) noexcept { return a == b.get(); }
template<typename T, typename U> constexpr auto operator<=>(const T* a, const lent_ptr<U>& b) noexcept { return a <=> b.get(); }
template<typename T, typename U> constexpr bool operator== (const T* a, const lent_ptr<U>& b) noexcept { return a == b.get(); }
//raw - lent
template<typename T> constexpr auto operator<=>(std::nullptr_t, const lent_ref<T>& b) noexcept { return nullptr <=> b.get(); }
template<typename T> constexpr bool operator== (std::nullptr_t, const lent_ref<T>& b) noexcept { return nullptr == b.get(); }
template<typename T> constexpr auto operator<=>(std::nullptr_t, const lent_ptr<T>& b) noexcept { return nullptr <=> b.get(); }
template<typename T> constexpr bool operator== (std::nullptr_t, const lent_ptr<T>& b) noexcept { return nullptr == b.get(); }
template<typename T>
class lendable_base
{
template<typename Ptr, typename SrcPtr, typename U> friend Ptr detail::build_copy(U* t, const SrcPtr& src) noexcept;
template<typename Ptr, typename SrcPtr, typename U> friend Ptr detail::build_move(U* t, SrcPtr& src) noexcept;
template<typename Ptr, typename SrcPtr, typename U> 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<typename T, typename... Args>
unique_ref<T> make_unique(Args&&... args) noexcept { return unique_ref<T>(new T(std::forward<Args>(args)...)); }
template<typename T, typename Deleter>
unique_ref<T> make_unique_del(T* p, Deleter del) noexcept { return unique_ref<T>(p, std::move(del)); }
template<typename T>
lent_ref<T> promote(lent_ptr<T> ptr) noexcept { return detail::build_move<lent_ref<T>>(ptr.get(), ptr); }
template<typename T>
unique_ref<T> promote(unique_ptr<T>&& ptr) noexcept
{
if constexpr (ptr_traits<T>::custom_deleter)
return detail::build_move_with_deleter<unique_ref<T>>(ptr.release(), ptr);
else
return detail::build_move<unique_ref<T>>(ptr.release(), ptr);
}
template<typename T>
unique_ptr<T> demote(unique_ref<T> ptr) noexcept
{
if constexpr (ptr_traits<T>::custom_deleter)
return detail::build_move_with_deleter<unique_ptr<T>>(ptr.release(), ptr);
else
return detail::build_move<unique_ptr<T>>(ptr.release(), ptr);
}
template<typename T>
lent_ptr<T> demote(lent_ref<T> ptr) noexcept { return detail::build_move<lent_ptr<T>>(ptr.get(), ptr); }
template<typename T>
lent_ptr<T> demote(const lendable<T>& ptr) noexcept { return detail::build_copy<lent_ptr<T>>((T*)&ptr.get(), ptr); }
template<typename T>
lent_ptr<T> demote(const lendable_base<T>& ptr) noexcept { return detail::build_copy<lent_ptr<T>>((T*)&ptr, ptr); }
template<typename T>
lent_ptr<T> lend(const unique_ptr<T>& ptr) noexcept { return detail::build_copy<lent_ptr<T>>(ptr.get(), ptr); }
template<typename T, typename U>
lent_ptr<T> lend(const unique_ptr<U>& ptr) noexcept { return detail::build_copy<lent_ptr<T>>(const_cast<T*>(static_cast<const T*>(ptr.get())), ptr); }
template<typename T>
lent_ref<T> lend(const unique_ref<T>& ptr) noexcept { return detail::build_copy<lent_ref<T>>(ptr.get(), ptr); }
template<typename T, typename U>
lent_ref<T> lend(const unique_ref<U>& ptr) noexcept { return detail::build_copy<lent_ref<T>>(const_cast<T*>(static_cast<const T*>(ptr.get())), ptr); }
template<typename T, typename U>
lent_ref<T> lend(const lendable_base<U>& ptr) noexcept { return detail::build_copy<lent_ref<T>>(const_cast<T*>(static_cast<const T*>(&ptr)), ptr); }
template<typename T, typename U>
lent_ref<T> lend(const lendable_base<U>* ptr) noexcept { return detail::build_copy<lent_ref<T>>(const_cast<T*>(static_cast<const T*>(ptr)), *ptr); }
template<typename T>
lent_ref<T> lend(const lendable_base<T>& ptr) noexcept { return detail::build_copy<lent_ref<T>>((T*)&ptr, ptr); }
template<typename T>
lent_ref<T> lend(const lendable_base<T>* ptr) noexcept { return detail::build_copy<lent_ref<T>>((T*)ptr, *ptr); }
template<typename T>
lent_ref<T> lend(const lendable<T>& ptr) noexcept { return detail::build_copy<lent_ref<T>>((T*)&ptr.get(), ptr); }
template<typename T>
lent_ref<T> lend(const T* ptr) noexcept { return detail::build_copy<lent_ref<T>>(const_cast<T*>(static_cast<const T*>(ptr)), *ptr); }
template<typename T>
lent_ref<T> promote(const unique_ptr<T>& ptr) noexcept { return detail::build_copy<lent_ref<T>>(ptr.get(), ptr); }
template<typename T, typename U>
lent_ptr<T> dynamic_lent_cast(lent_ptr<U> ptr) noexcept { return detail::build_move<lent_ptr<T>>(dynamic_cast<T*>(ptr.get()), ptr); }
template<typename T, typename U>
lent_ptr<T> dynamic_lent_cast(lent_ref<U> ptr) noexcept { return detail::build_move<lent_ptr<T>>(dynamic_cast<T*>(ptr.get()), ptr); }
template<typename T, typename U>
lent_ptr<T> dynamic_lent_cast(const unique_ptr<U>& ptr) noexcept { return detail::build_copy<lent_ptr<T>>(dynamic_cast<T*>(ptr.get()), ptr); }
template<typename T, typename U>
lent_ptr<T> dynamic_lent_cast(const unique_ref<U>& ptr) noexcept { return detail::build_copy<lent_ptr<T>>(dynamic_cast<T*>(ptr.get()), ptr); }
template<typename T, typename U>
lent_ptr<T> static_lent_cast(lent_ptr<U> ptr) noexcept { return detail::build_move<lent_ptr<T>>(static_cast<T*>(ptr.get()), ptr); }
template<typename T, typename U>
lent_ref<T> static_lent_cast(lent_ref<U> ptr) noexcept { return detail::build_move<lent_ref<T>>(static_cast<T*>(ptr.get()), ptr); }
template<typename T, typename U>
lent_ptr<T> static_lent_cast(const unique_ptr<U>& ptr) noexcept { return detail::build_copy<lent_ptr<T>>(static_cast<T*>(ptr.get()), ptr); }
template<typename T, typename U>
lent_ref<T> static_lent_cast(const unique_ref<U>& ptr) noexcept { return detail::build_copy<lent_ref<T>>(static_cast<T*>(ptr.get()), ptr); }
template<typename T, typename U>
unique_ptr<T> dynamic_unique_cast(unique_ptr<U> ptr) noexcept
{
auto* p = ptr.release();
if (auto* t = dynamic_cast<T*>(p))
{
if constexpr (ptr_traits<T>::custom_deleter)
return detail::build_move_with_deleter<unique_ptr<T>>(t, ptr);
else
return detail::build_move<unique_ptr<T>>(t, ptr);
}
delete p;
return nullptr;
}
template<typename T, typename U>
unique_ptr<T> dynamic_unique_cast(unique_ref<U> ptr) noexcept
{
auto* p = ptr.release();
if (auto* t = dynamic_cast<T*>(p))
{
if constexpr (ptr_traits<T>::custom_deleter)
return detail::build_move_with_deleter<unique_ptr<T>>(t, ptr);
else
return detail::build_move<unique_ptr<T>>(t, ptr);
}
delete p;
return nullptr;
}
template<typename T, typename U>
lent_ref<T> const_lent_cast(lent_ref<U> ref) noexcept { return detail::build_copy<lent_ref<T>>(const_cast<T*>(ref.get()), ref); }
template<typename T, typename U>
lent_ptr<T> const_lent_cast(lent_ptr<U> ptr) noexcept { return detail::build_copy<lent_ptr<T>>(const_cast<T*>(ptr.get()), ptr); }
}
namespace std
{
template<typename T> struct hash<tl::lent_ref<T>>
{
size_t operator()(const tl::lent_ref<T>& ptr) const noexcept
{
std::hash<T*> hasher;
return hasher(ptr.get());
}
};
template<typename T> struct hash<tl::lent_ptr<T>>
{
size_t operator()(const tl::lent_ptr<T>& ptr) const noexcept
{
std::hash<T*> hasher;
return hasher(ptr.get());
}
};
template<typename T> struct hash<tl::unique_ref<T>>
{
size_t operator()(const tl::unique_ref<T>& ptr) const noexcept
{
std::hash<T*> hasher;
return hasher(ptr.get());
}
};
template<typename T> struct hash<tl::unique_ptr<T>>
{
size_t operator()(const tl::unique_ptr<T>& ptr) const noexcept
{
std::hash<T*> hasher;
return hasher(ptr.get());
}
};
}
namespace eastl
{
template<typename T> struct hash<tl::lent_ref<T>>
{
size_t operator()(const tl::lent_ref<T>& ptr) const noexcept
{
eastl::hash<T*> hasher;
return hasher(ptr.get());
}
};
template<typename T> struct hash<tl::lent_ptr<T>>
{
size_t operator()(const tl::lent_ptr<T>& ptr) const noexcept
{
eastl::hash<T*> hasher;
return hasher(ptr.get());
}
};
template<typename T> struct hash<tl::unique_ref<T>>
{
size_t operator()(const tl::unique_ref<T>& ptr) const noexcept
{
eastl::hash<T*> hasher;
return hasher(ptr.get());
}
};
template<typename T> struct hash<tl::unique_ptr<T>>
{
size_t operator()(const tl::unique_ptr<T>& ptr) const noexcept
{
eastl::hash<T*> hasher;
return hasher(ptr.get());
}
};
#undef CHECK_NULL
}