shared_ptr.hpp: fix is_same_ptr<>() trait

Implemented as constexpr function.
This commit is contained in:
Nekotekina 2020-12-13 10:44:58 +03:00
parent f83c2f0b6b
commit 09d293592e

View file

@ -6,23 +6,47 @@
namespace stx namespace stx
{ {
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundefined_var_template"
#endif
// Not defined anywhere (and produces a useless warning)
template <typename X>
extern X sample;
// Checks whether the cast between two types is the same pointer
template <typename T, typename U> template <typename T, typename U>
constexpr bool is_same_ptr() noexcept constexpr bool is_same_ptr() noexcept
{ {
// I would like to make it a trait if there is some trick. if constexpr (std::is_void_v<T> || std::is_void_v<U> || std::is_same_v<T, U>)
// And believe it shall possible with constexpr bit_cast. {
// Otherwise I hope it will compile in null code anyway. return true;
const auto u = reinterpret_cast<U*>(0x11223344556); }
const volatile void* x = u; else if constexpr (std::is_convertible_v<U*, T*>)
return static_cast<T*>(u) == x; {
const auto u = &sample<U>;
const volatile void* x = u;
return static_cast<T*>(u) == x;
}
else if constexpr (std::is_convertible_v<T*, U*>)
{
const auto t = &sample<T>;
const volatile void* x = t;
return static_cast<U*>(t) == x;
}
else
{
return false;
}
} }
// TODO
template <typename T, typename U> template <typename T, typename U>
constexpr bool is_same_ptr_v = true; constexpr bool is_same_ptr_cast_v = std::is_convertible_v<U*, T*> && is_same_ptr<T, U>();
template <typename T, typename U> #ifdef __clang__
constexpr bool is_same_ptr_cast_v = std::is_same_v<T, U> || (std::is_convertible_v<U, T> && is_same_ptr_v<T, U>); #pragma clang diagnostic pop
#endif
template <typename T> template <typename T>
class single_ptr; class single_ptr;
@ -100,6 +124,9 @@ namespace stx
return std::launder(reinterpret_cast<shared_counter*>(reinterpret_cast<u64>(m_ptr) - sizeof(shared_counter))); return std::launder(reinterpret_cast<shared_counter*>(reinterpret_cast<u64>(m_ptr) - sizeof(shared_counter)));
} }
template <typename U>
friend class single_ptr;
template <typename U> template <typename U>
friend class shared_ptr; friend class shared_ptr;
@ -125,7 +152,6 @@ namespace stx
single_ptr(single_ptr<U>&& r) noexcept single_ptr(single_ptr<U>&& r) noexcept
: m_ptr(r.m_ptr) : m_ptr(r.m_ptr)
{ {
ensure(is_same_ptr<T, U>());
r.m_ptr = nullptr; r.m_ptr = nullptr;
} }
@ -146,7 +172,6 @@ namespace stx
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>> template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>>
single_ptr& operator=(single_ptr<U>&& r) noexcept single_ptr& operator=(single_ptr<U>&& r) noexcept
{ {
ensure(is_same_ptr<T, U>());
m_ptr = r.m_ptr; m_ptr = r.m_ptr;
r.m_ptr = nullptr; r.m_ptr = nullptr;
return *this; return *this;
@ -216,11 +241,9 @@ namespace stx
} }
// "Moving" "static cast" // "Moving" "static cast"
template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr_v<U, T>>> template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr<U, T>()>>
explicit operator single_ptr<U>() && noexcept explicit operator single_ptr<U>() && noexcept
{ {
ensure(is_same_ptr<U, T>());
single_ptr<U> r; single_ptr<U> r;
r.m_ptr = static_cast<decltype(r.m_ptr)>(std::exchange(m_ptr, nullptr)); r.m_ptr = static_cast<decltype(r.m_ptr)>(std::exchange(m_ptr, nullptr));
return r; return r;
@ -348,6 +371,9 @@ namespace stx
return std::launder(reinterpret_cast<shared_counter*>(reinterpret_cast<u64>(m_ptr) - sizeof(shared_counter))); return std::launder(reinterpret_cast<shared_counter*>(reinterpret_cast<u64>(m_ptr) - sizeof(shared_counter)));
} }
template <typename U>
friend class shared_ptr;
template <typename U> template <typename U>
friend class atomic_ptr; friend class atomic_ptr;
@ -369,7 +395,6 @@ namespace stx
shared_ptr(const shared_ptr<U>& r) noexcept shared_ptr(const shared_ptr<U>& r) noexcept
: m_ptr(r.m_ptr) : m_ptr(r.m_ptr)
{ {
ensure(is_same_ptr<T, U>());
if (m_ptr) if (m_ptr)
d()->refs++; d()->refs++;
} }
@ -384,7 +409,6 @@ namespace stx
shared_ptr(shared_ptr<U>&& r) noexcept shared_ptr(shared_ptr<U>&& r) noexcept
: m_ptr(r.m_ptr) : m_ptr(r.m_ptr)
{ {
ensure(is_same_ptr<T, U>());
r.m_ptr = nullptr; r.m_ptr = nullptr;
} }
@ -392,7 +416,6 @@ namespace stx
shared_ptr(single_ptr<U>&& r) noexcept shared_ptr(single_ptr<U>&& r) noexcept
: m_ptr(r.m_ptr) : m_ptr(r.m_ptr)
{ {
ensure(is_same_ptr<T, U>());
r.m_ptr = nullptr; r.m_ptr = nullptr;
} }
@ -410,7 +433,6 @@ namespace stx
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>> template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>>
shared_ptr& operator=(const shared_ptr<U>& r) noexcept shared_ptr& operator=(const shared_ptr<U>& r) noexcept
{ {
ensure(is_same_ptr<T, U>());
shared_ptr(r).swap(*this); shared_ptr(r).swap(*this);
return *this; return *this;
} }
@ -424,7 +446,6 @@ namespace stx
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>> template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>>
shared_ptr& operator=(shared_ptr<U>&& r) noexcept shared_ptr& operator=(shared_ptr<U>&& r) noexcept
{ {
ensure(is_same_ptr<T, U>());
shared_ptr(std::move(r)).swap(*this); shared_ptr(std::move(r)).swap(*this);
return *this; return *this;
} }
@ -432,7 +453,6 @@ namespace stx
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>> template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>>
shared_ptr& operator=(single_ptr<U>&& r) noexcept shared_ptr& operator=(single_ptr<U>&& r) noexcept
{ {
ensure(is_same_ptr<T, U>());
shared_ptr(std::move(r)).swap(*this); shared_ptr(std::move(r)).swap(*this);
return *this; return *this;
} }
@ -450,11 +470,9 @@ namespace stx
} }
// Converts to unique (single) ptr if reference is 1, otherwise returns null. Nullifies self. // Converts to unique (single) ptr if reference is 1, otherwise returns null. Nullifies self.
template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr_v<U, T>>> template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr<U, T>()>>
explicit operator single_ptr<U>() && noexcept explicit operator single_ptr<U>() && noexcept
{ {
ensure(is_same_ptr<U, T>());
const auto o = d(); const auto o = d();
if (m_ptr && !--o->refs) if (m_ptr && !--o->refs)
@ -537,11 +555,9 @@ namespace stx
} }
// Basic "static cast" support // Basic "static cast" support
template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr_v<U, T>>> template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr<U, T>()>>
explicit operator shared_ptr<U>() const& noexcept explicit operator shared_ptr<U>() const& noexcept
{ {
ensure(is_same_ptr<U, T>());
if (m_ptr) if (m_ptr)
{ {
d()->refs++; d()->refs++;
@ -553,11 +569,9 @@ namespace stx
} }
// "Moving" "static cast" // "Moving" "static cast"
template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr_v<U, T>>> template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr<U, T>()>>
explicit operator shared_ptr<U>() && noexcept explicit operator shared_ptr<U>() && noexcept
{ {
ensure(is_same_ptr<U, T>());
shared_ptr<U> r; shared_ptr<U> r;
r.m_ptr = static_cast<decltype(r.m_ptr)>(std::exchange(m_ptr, nullptr)); r.m_ptr = static_cast<decltype(r.m_ptr)>(std::exchange(m_ptr, nullptr));
return r; return r;
@ -592,6 +606,9 @@ namespace stx
return d(m_val); return d(m_val);
} }
template <typename U>
friend class atomic_ptr;
public: public:
using pointer = T*; using pointer = T*;
@ -614,8 +631,6 @@ namespace stx
atomic_ptr(const shared_ptr<U>& r) noexcept atomic_ptr(const shared_ptr<U>& r) noexcept
: m_val(reinterpret_cast<uptr>(r.m_ptr) << c_ref_size) : m_val(reinterpret_cast<uptr>(r.m_ptr) << c_ref_size)
{ {
ensure(is_same_ptr<T, U>());
// Obtain a ref + as many refs as an atomic_ptr can additionally reference // Obtain a ref + as many refs as an atomic_ptr can additionally reference
if (m_val) if (m_val)
d()->refs += c_ref_mask + 1; d()->refs += c_ref_mask + 1;
@ -625,7 +640,6 @@ namespace stx
atomic_ptr(shared_ptr<U>&& r) noexcept atomic_ptr(shared_ptr<U>&& r) noexcept
: m_val(reinterpret_cast<uptr>(r.m_ptr) << c_ref_size) : m_val(reinterpret_cast<uptr>(r.m_ptr) << c_ref_size)
{ {
ensure(is_same_ptr<T, U>());
r.m_ptr = nullptr; r.m_ptr = nullptr;
if (m_val) if (m_val)
@ -636,7 +650,6 @@ namespace stx
atomic_ptr(single_ptr<U>&& r) noexcept atomic_ptr(single_ptr<U>&& r) noexcept
: m_val(reinterpret_cast<uptr>(r.m_ptr) << c_ref_size) : m_val(reinterpret_cast<uptr>(r.m_ptr) << c_ref_size)
{ {
ensure(is_same_ptr<T, U>());
r.m_ptr = nullptr; r.m_ptr = nullptr;
if (m_val) if (m_val)
@ -668,7 +681,6 @@ namespace stx
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>> template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>>
atomic_ptr& operator=(const shared_ptr<U>& r) noexcept atomic_ptr& operator=(const shared_ptr<U>& r) noexcept
{ {
ensure(is_same_ptr<T, U>());
store(r); store(r);
return *this; return *this;
} }
@ -676,7 +688,6 @@ namespace stx
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>> template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>>
atomic_ptr& operator=(shared_ptr<U>&& r) noexcept atomic_ptr& operator=(shared_ptr<U>&& r) noexcept
{ {
ensure(is_same_ptr<T, U>());
store(std::move(r)); store(std::move(r));
return *this; return *this;
} }
@ -684,7 +695,6 @@ namespace stx
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>> template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>>
atomic_ptr& operator=(single_ptr<U>&& r) noexcept atomic_ptr& operator=(single_ptr<U>&& r) noexcept
{ {
ensure(is_same_ptr<T, U>());
store(std::move(r)); store(std::move(r));
return *this; return *this;
} }
@ -948,8 +958,6 @@ namespace stx
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>> template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>>
shared_type compare_and_swap(const shared_ptr<U>& cmp, shared_type exch) shared_type compare_and_swap(const shared_ptr<U>& cmp, shared_type exch)
{ {
ensure(is_same_ptr<T, U>());
shared_type old = cmp; shared_type old = cmp;
if (compare_exchange(old, std::move(exch))) if (compare_exchange(old, std::move(exch)))
@ -966,8 +974,6 @@ namespace stx
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>> template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>>
bool compare_and_swap_test(const shared_ptr<U>& cmp, shared_type exch) bool compare_and_swap_test(const shared_ptr<U>& cmp, shared_type exch)
{ {
ensure(is_same_ptr<T, U>());
const uptr _old = reinterpret_cast<uptr>(cmp.m_ptr); const uptr _old = reinterpret_cast<uptr>(cmp.m_ptr);
const uptr _new = reinterpret_cast<uptr>(exch.m_ptr); const uptr _new = reinterpret_cast<uptr>(exch.m_ptr);
@ -1007,8 +1013,6 @@ namespace stx
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>> template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U>>>
shared_type compare_and_swap(const single_ptr<U>& cmp, shared_type exch) shared_type compare_and_swap(const single_ptr<U>& cmp, shared_type exch)
{ {
ensure(is_same_ptr<T, U>());
shared_type old = cmp; shared_type old = cmp;
if (compare_exchange(old, std::move(exch))) if (compare_exchange(old, std::move(exch)))