1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-26 04:32:35 +01:00

shared_ptr.hpp: further fixes

Improved is_same_ptr() trait (added third "maybe" category).
Fallback to pseudo-runtime check in certain cases (if "maybe").
Fix single_ptr moving assignment operator.
This commit is contained in:
Nekotekina 2021-04-08 18:45:43 +03:00
parent 332c512eed
commit 8c5f2b249c
2 changed files with 124 additions and 53 deletions

View File

@ -25,16 +25,23 @@ namespace stx
static thread_local const
#endif
fake_t<std::remove_cv_t<T>> sample{};
template <typename From, typename To, typename = void>
struct can_static_cast : std::false_type {};
template <typename From, typename To>
struct can_static_cast<From, To, std::void_t<decltype(static_cast<To>(std::declval<From>()))>> : std::true_type {};
}
// Classify compile-time information available for pointers
enum class same_ptr
{
no,
yes,
maybe
};
template <typename X, typename Y>
constexpr bool is_same_ptr_test(const volatile Y*, ...)
{
// Can't sample abstract class
return false;
}
template <typename X, typename Y, std::enable_if_t<!std::is_abstract_v<Y>, int> = 0>
constexpr bool is_same_ptr_test(const volatile Y* ptr = std::addressof(detail::sample<Y>.data))
{
return static_cast<const volatile X*>(ptr) == static_cast<const volatile void*>(ptr);
@ -42,26 +49,26 @@ namespace stx
// Checks whether the cast between two types is the same pointer
template <typename T, typename U>
constexpr bool is_same_ptr() noexcept
constexpr same_ptr is_same_ptr() noexcept
{
if constexpr (std::is_void_v<T> || std::is_void_v<U> || std::is_same_v<T, U>)
if constexpr (std::is_void_v<T> || std::is_void_v<U> || std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<U>>)
{
return true;
return same_ptr::yes;
}
else if constexpr (std::is_convertible_v<U*, T*>)
else if constexpr (detail::can_static_cast<U*, T*>::value && !std::is_abstract_v<U>)
{
return is_same_ptr_test<T, U>();
return is_same_ptr_test<T, U>() ? same_ptr::yes : same_ptr::no;
}
else if constexpr (std::is_convertible_v<T*, U*>)
else if constexpr (detail::can_static_cast<T*, U*>::value && !std::is_abstract_v<T>)
{
return is_same_ptr_test<U, T>();
return is_same_ptr_test<U, T>() ? same_ptr::yes : same_ptr::no;
}
return !std::is_class_v<T> && !std::is_class_v<U> && !std::is_union_v<T> && !std::is_union_v<U>;
return same_ptr::maybe;
}
template <typename T, typename U>
constexpr bool is_same_ptr_cast_v = std::is_convertible_v<U*, T*> && is_same_ptr<T, U>();
constexpr same_ptr is_same_ptr_cast_v = std::is_convertible_v<U*, T*> ? is_same_ptr<T, U>() : same_ptr::no;
template <typename T>
class single_ptr;
@ -166,10 +173,13 @@ namespace stx
r.m_ptr = nullptr;
}
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> != same_ptr::no>>
single_ptr(single_ptr<U>&& r) noexcept
: m_ptr(r.m_ptr)
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(r.m_ptr));
m_ptr = r.m_ptr;
r.m_ptr = nullptr;
}
@ -182,16 +192,17 @@ namespace stx
single_ptr& operator=(single_ptr&& r) noexcept
{
m_ptr = r.m_ptr;
r.m_ptr = nullptr;
single_ptr(std::move(r)).swap(*this);
return *this;
}
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> != same_ptr::no>>
single_ptr& operator=(single_ptr<U>&& r) noexcept
{
m_ptr = r.m_ptr;
r.m_ptr = nullptr;
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(r.m_ptr));
single_ptr(std::move(r)).swap(*this);
return *this;
}
@ -259,9 +270,12 @@ namespace stx
}
// "Moving" "static cast"
template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr<U, T>()>>
template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr<U, T>() != same_ptr::no>>
explicit operator single_ptr<U>() && noexcept
{
if constexpr (is_same_ptr<U, T>() == same_ptr::maybe)
ensure(is_same_ptr_test<U, T>(m_ptr));
single_ptr<U> r;
r.m_ptr = static_cast<decltype(r.m_ptr)>(std::exchange(m_ptr, nullptr));
return r;
@ -424,10 +438,13 @@ namespace stx
d()->refs++;
}
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> != same_ptr::no>>
shared_ptr(const shared_ptr<U>& r) noexcept
: m_ptr(r.m_ptr)
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(r.m_ptr));
m_ptr = r.m_ptr;
if (m_ptr)
d()->refs++;
}
@ -438,17 +455,23 @@ namespace stx
r.m_ptr = nullptr;
}
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> != same_ptr::no>>
shared_ptr(shared_ptr<U>&& r) noexcept
: m_ptr(r.m_ptr)
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(r.m_ptr));
m_ptr = r.m_ptr;
r.m_ptr = nullptr;
}
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> != same_ptr::no>>
shared_ptr(single_ptr<U>&& r) noexcept
: m_ptr(r.m_ptr)
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(r.m_ptr));
m_ptr = r.m_ptr;
r.m_ptr = nullptr;
}
@ -463,9 +486,12 @@ namespace stx
return *this;
}
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> != same_ptr::no>>
shared_ptr& operator=(const shared_ptr<U>& r) noexcept
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(r.m_ptr));
shared_ptr(r).swap(*this);
return *this;
}
@ -476,16 +502,22 @@ namespace stx
return *this;
}
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> != same_ptr::no>>
shared_ptr& operator=(shared_ptr<U>&& r) noexcept
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(r.m_ptr));
shared_ptr(std::move(r)).swap(*this);
return *this;
}
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> != same_ptr::no>>
shared_ptr& operator=(single_ptr<U>&& r) noexcept
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(r.m_ptr));
shared_ptr(std::move(r)).swap(*this);
return *this;
}
@ -503,9 +535,12 @@ namespace stx
}
// 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<U, T>()>>
template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr<U, T>() != same_ptr::no>>
explicit operator single_ptr<U>() && noexcept
{
if constexpr (is_same_ptr<U, T>() == same_ptr::maybe)
ensure(is_same_ptr_test<U, T>(m_ptr));
const auto o = d();
if (m_ptr && !--o->refs)
@ -588,9 +623,12 @@ namespace stx
}
// Basic "static cast" support
template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr<U, T>()>>
template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr<U, T>() != same_ptr::no>>
explicit operator shared_ptr<U>() const& noexcept
{
if constexpr (is_same_ptr<U, T>() == same_ptr::maybe)
ensure(is_same_ptr_test<U, T>(m_ptr));
if (m_ptr)
{
d()->refs++;
@ -602,9 +640,12 @@ namespace stx
}
// "Moving" "static cast"
template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr<U, T>()>>
template <typename U, typename = decltype(static_cast<U*>(std::declval<T*>())), typename = std::enable_if_t<is_same_ptr<U, T>() != same_ptr::no>>
explicit operator shared_ptr<U>() && noexcept
{
if constexpr (is_same_ptr<U, T>() == same_ptr::maybe)
ensure(is_same_ptr_test<U, T>(m_ptr));
shared_ptr<U> r;
r.m_ptr = static_cast<decltype(r.m_ptr)>(std::exchange(m_ptr, nullptr));
return r;
@ -660,29 +701,38 @@ namespace stx
d()->refs.raw() += c_ref_mask;
}
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> != same_ptr::no>>
atomic_ptr(const shared_ptr<U>& r) noexcept
: m_val(reinterpret_cast<uptr>(r.m_ptr) << c_ref_size)
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(r.m_ptr));
// Obtain a ref + as many refs as an atomic_ptr can additionally reference
m_val = reinterpret_cast<uptr>(r.m_ptr) << c_ref_size;
if (m_val)
d()->refs += c_ref_mask + 1;
}
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> != same_ptr::no>>
atomic_ptr(shared_ptr<U>&& r) noexcept
: m_val(reinterpret_cast<uptr>(r.m_ptr) << c_ref_size)
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(r.m_ptr));
m_val = reinterpret_cast<uptr>(r.m_ptr) << c_ref_size;
r.m_ptr = nullptr;
if (m_val)
d()->refs += c_ref_mask;
}
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> != same_ptr::no>>
atomic_ptr(single_ptr<U>&& r) noexcept
: m_val(reinterpret_cast<uptr>(r.m_ptr) << c_ref_size)
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(r.m_ptr));
m_val = reinterpret_cast<uptr>(r.m_ptr) << c_ref_size;
r.m_ptr = nullptr;
if (m_val)
@ -711,23 +761,32 @@ namespace stx
return *this;
}
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> != same_ptr::no>>
atomic_ptr& operator=(const shared_ptr<U>& r) noexcept
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(r.m_ptr));
store(r);
return *this;
}
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> != same_ptr::no>>
atomic_ptr& operator=(shared_ptr<U>&& r) noexcept
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(r.m_ptr));
store(std::move(r));
return *this;
}
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> != same_ptr::no>>
atomic_ptr& operator=(single_ptr<U>&& r) noexcept
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(r.m_ptr));
store(std::move(r));
return *this;
}
@ -988,9 +1047,12 @@ namespace stx
}
// Unoptimized
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> != same_ptr::no>>
shared_type compare_and_swap(const shared_ptr<U>& cmp, shared_type exch)
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(cmp.m_ptr));
shared_type old = cmp;
if (compare_exchange(old, std::move(exch)))
@ -1004,9 +1066,12 @@ namespace stx
}
// More lightweight than compare_exchange
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> != same_ptr::no>>
bool compare_and_swap_test(const shared_ptr<U>& cmp, shared_type exch)
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(cmp.m_ptr));
const uptr _old = reinterpret_cast<uptr>(cmp.m_ptr);
const uptr _new = reinterpret_cast<uptr>(exch.m_ptr);
@ -1043,9 +1108,12 @@ namespace stx
}
// Unoptimized
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> != same_ptr::no>>
shared_type compare_and_swap(const single_ptr<U>& cmp, shared_type exch)
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(cmp.m_ptr));
shared_type old = cmp;
if (compare_exchange(old, std::move(exch)))
@ -1059,9 +1127,12 @@ namespace stx
}
// Supplementary
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> != same_ptr::no>>
bool compare_and_swap_test(const single_ptr<U>& cmp, shared_type exch)
{
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(cmp.m_ptr));
return compare_and_swap_test(reinterpret_cast<const shared_ptr<U>&>(cmp), std::move(exch));
}
@ -1111,13 +1182,13 @@ namespace stx
return m_val != 0;
}
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> != same_ptr::no>>
bool is_equal(const shared_ptr<U>& r) const noexcept
{
return observe() == r.get();
}
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> != same_ptr::no>>
bool is_equal(const single_ptr<U>& r) const noexcept
{
return observe() == r.get();

View File

@ -255,7 +255,7 @@ namespace stx
else
{
static_assert(sizeof(As) > 0);
static_assert(is_same_ptr<T, As>());
static_assert(is_same_ptr<T, As>() == same_ptr::yes); // TODO
return type_counter<Info>::template dyn_type<T, As>.index();
}
}
@ -274,7 +274,7 @@ namespace stx
ATTR_PURE inline const Info& typedata() noexcept
{
static_assert(sizeof(T) > 0 && sizeof(As) > 0);
static_assert(is_same_ptr<T, As>());
static_assert(is_same_ptr<T, As>() == same_ptr::yes); // TODO
return type_counter<Info>::template dyn_type<T, As>;
}