1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-22 02:32:36 +01:00

shared_ptr.hpp: don't use fake objects

This lifts the limitation for casting with abstract classes.
Use new C++20 feature (constexpr allocator) to test viability.
Add SamePtr concept to types.hpp
This commit is contained in:
Nekotekina 2021-05-29 11:21:57 +03:00
parent eec9578619
commit f5e529db61
4 changed files with 130 additions and 149 deletions

View File

@ -11,3 +11,52 @@ static_assert(be_t<u16>(1) + be_t<u32>(2) + be_t<u64>(3) == 6);
static_assert(le_t<u16>(1) + le_t<u32>(2) + le_t<u64>(3) == 6); static_assert(le_t<u16>(1) + le_t<u32>(2) + le_t<u64>(3) == 6);
static_assert(sizeof(nullptr) == sizeof(void*)); static_assert(sizeof(nullptr) == sizeof(void*));
static_assert(__cpp_constexpr_dynamic_alloc >= 201907L);
namespace
{
struct A
{
int a;
};
struct B : A
{
int b;
};
struct Z
{
};
struct C
{
virtual ~C() = 0;
int C;
};
struct D : Z, B
{
int d;
};
struct E : C, B
{
int e;
};
struct F : C
{
virtual ~F() = 0;
};
static_assert(is_same_ptr<B, A>());
static_assert(is_same_ptr<A, B>());
static_assert(is_same_ptr<D, B>());
static_assert(is_same_ptr<B, D>());
static_assert(!is_same_ptr<E, B>());
static_assert(!is_same_ptr<B, E>());
static_assert(is_same_ptr<F, C>());
static_assert(is_same_ptr<C, F>());
}

View File

@ -6,63 +6,8 @@
namespace stx namespace stx
{ {
namespace detail template <typename To, typename From>
{ constexpr bool same_ptr_implicit_v = std::is_convertible_v<const volatile From*, const volatile To*> ? is_same_ptr<From, To>() : false;
template <typename T>
union fake_t
{
char dummy;
T data;
fake_t() noexcept {}
~fake_t() {}
};
template <typename T>
#ifdef _MSC_VER
static const
#else
static thread_local const
#endif
fake_t<std::remove_cv_t<T>> sample{};
}
// 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* ptr = std::addressof(detail::sample<Y>.data))
{
return static_cast<const volatile X*>(ptr) == static_cast<const volatile void*>(ptr);
}
// Checks whether the cast between two types is the same pointer
template <typename T, typename U>
constexpr same_ptr is_same_ptr() noexcept
{
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 same_ptr::yes;
}
else if constexpr (PtrCastable<T, U> && !std::is_abstract_v<U>)
{
return is_same_ptr_test<T, U>() ? same_ptr::yes : same_ptr::no;
}
else if constexpr (PtrCastable<T, U> && !std::is_abstract_v<T>)
{
return is_same_ptr_test<T, U>() ? same_ptr::yes : same_ptr::no;
}
return same_ptr::maybe;
}
template <typename T, typename 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> template <typename T>
class single_ptr; class single_ptr;
@ -175,12 +120,9 @@ namespace stx
r.m_ptr = nullptr; r.m_ptr = nullptr;
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
single_ptr(single_ptr<U>&& r) noexcept single_ptr(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));
m_ptr = r.m_ptr; m_ptr = r.m_ptr;
r.m_ptr = nullptr; r.m_ptr = nullptr;
} }
@ -200,12 +142,9 @@ namespace stx
return *this; return *this;
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
single_ptr& operator=(single_ptr<U>&& r) noexcept single_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));
single_ptr(std::move(r)).swap(*this); single_ptr(std::move(r)).swap(*this);
return *this; return *this;
} }
@ -269,12 +208,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<U, T>() != same_ptr::no>> template <typename U> requires PtrSame<T, U>
explicit operator single_ptr<U>() && noexcept 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; 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;
@ -436,12 +372,9 @@ namespace stx
ensure((d()->refs++ - 1) >> 58 == 0); ensure((d()->refs++ - 1) >> 58 == 0);
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
shared_ptr(const shared_ptr<U>& r) noexcept shared_ptr(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));
m_ptr = r.m_ptr; m_ptr = r.m_ptr;
if (m_ptr) if (m_ptr)
d()->refs++; d()->refs++;
@ -453,22 +386,16 @@ namespace stx
r.m_ptr = nullptr; r.m_ptr = nullptr;
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
shared_ptr(shared_ptr<U>&& r) noexcept shared_ptr(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));
m_ptr = r.m_ptr; m_ptr = r.m_ptr;
r.m_ptr = nullptr; r.m_ptr = nullptr;
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
shared_ptr(single_ptr<U>&& r) noexcept shared_ptr(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));
m_ptr = r.m_ptr; m_ptr = r.m_ptr;
r.m_ptr = nullptr; r.m_ptr = nullptr;
} }
@ -486,12 +413,9 @@ namespace stx
[[deprecated("Use null_ptr")]] shared_ptr& operator=(std::nullptr_t) = delete; [[deprecated("Use null_ptr")]] shared_ptr& operator=(std::nullptr_t) = delete;
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
shared_ptr& operator=(const shared_ptr<U>& r) noexcept 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); shared_ptr(r).swap(*this);
return *this; return *this;
} }
@ -502,22 +426,16 @@ namespace stx
return *this; return *this;
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
shared_ptr& operator=(shared_ptr<U>&& r) noexcept 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); shared_ptr(std::move(r)).swap(*this);
return *this; return *this;
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
shared_ptr& operator=(single_ptr<U>&& r) noexcept 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); shared_ptr(std::move(r)).swap(*this);
return *this; return *this;
} }
@ -535,12 +453,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<U, T>() != same_ptr::no>> template <typename U> requires PtrSame<T, U>
explicit operator single_ptr<U>() && noexcept 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(); const auto o = d();
if (m_ptr && !--o->refs) if (m_ptr && !--o->refs)
@ -618,12 +533,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<U, T>() != same_ptr::no>> template <typename U> requires PtrSame<T, U>
explicit operator shared_ptr<U>() const& noexcept 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) if (m_ptr)
{ {
d()->refs++; d()->refs++;
@ -635,12 +547,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<U, T>() != same_ptr::no>> template <typename U> requires PtrSame<T, U>
explicit operator shared_ptr<U>() && noexcept 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; 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;
@ -702,24 +611,18 @@ namespace stx
d()->refs.raw() += c_ref_mask; d()->refs.raw() += c_ref_mask;
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
atomic_ptr(const shared_ptr<U>& r) noexcept atomic_ptr(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));
// 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
m_val = reinterpret_cast<uptr>(r.m_ptr) << c_ref_size; m_val = reinterpret_cast<uptr>(r.m_ptr) << c_ref_size;
if (m_val) if (m_val)
d()->refs += c_ref_mask + 1; d()->refs += c_ref_mask + 1;
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
atomic_ptr(shared_ptr<U>&& r) noexcept atomic_ptr(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));
m_val = reinterpret_cast<uptr>(r.m_ptr) << c_ref_size; m_val = reinterpret_cast<uptr>(r.m_ptr) << c_ref_size;
r.m_ptr = nullptr; r.m_ptr = nullptr;
@ -727,12 +630,9 @@ namespace stx
d()->refs += c_ref_mask; d()->refs += c_ref_mask;
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
atomic_ptr(single_ptr<U>&& r) noexcept atomic_ptr(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));
m_val = reinterpret_cast<uptr>(r.m_ptr) << c_ref_size; m_val = reinterpret_cast<uptr>(r.m_ptr) << c_ref_size;
r.m_ptr = nullptr; r.m_ptr = nullptr;
@ -762,32 +662,23 @@ namespace stx
return *this; return *this;
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
atomic_ptr& operator=(const shared_ptr<U>& r) noexcept 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); store(r);
return *this; return *this;
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
atomic_ptr& operator=(shared_ptr<U>&& r) noexcept 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)); store(std::move(r));
return *this; return *this;
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
atomic_ptr& operator=(single_ptr<U>&& r) noexcept 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)); store(std::move(r));
return *this; return *this;
} }
@ -1048,24 +939,18 @@ namespace stx
} }
// Unoptimized // Unoptimized
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_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)
{ {
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(cmp.m_ptr));
shared_type old = cmp; shared_type old = cmp;
static_cast<void>(compare_exchange(old, std::move(exch))); static_cast<void>(compare_exchange(old, std::move(exch)));
return old; return old;
} }
// More lightweight than compare_exchange // More lightweight than compare_exchange
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_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)
{ {
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 _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);
@ -1102,24 +987,18 @@ namespace stx
} }
// Unoptimized // Unoptimized
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_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)
{ {
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
ensure(is_same_ptr_test<T, U>(cmp.m_ptr));
shared_type old = cmp; shared_type old = cmp;
static_cast<void>(compare_exchange(old, std::move(exch))); static_cast<void>(compare_exchange(old, std::move(exch)));
return old; return old;
} }
// Supplementary // Supplementary
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
bool compare_and_swap_test(const single_ptr<U>& cmp, shared_type exch) 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)); return compare_and_swap_test(reinterpret_cast<const shared_ptr<U>&>(cmp), std::move(exch));
} }
@ -1169,13 +1048,13 @@ namespace stx
return m_val != 0; return m_val != 0;
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
bool is_equal(const shared_ptr<U>& r) const noexcept bool is_equal(const shared_ptr<U>& r) const noexcept
{ {
return observe() == r.get(); return observe() == r.get();
} }
template <typename U, typename = std::enable_if_t<is_same_ptr_cast_v<T, U> != same_ptr::no>> template <typename U> requires same_ptr_implicit_v<T, U>
bool is_equal(const single_ptr<U>& r) const noexcept bool is_equal(const single_ptr<U>& r) const noexcept
{ {
return observe() == r.get(); return observe() == r.get();

View File

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

View File

@ -9,6 +9,7 @@
#include <array> #include <array>
#include <tuple> #include <tuple>
#include <compare> #include <compare>
#include <memory>
#include <bit> #include <bit>
using std::chrono::steady_clock; using std::chrono::steady_clock;
@ -1017,3 +1018,55 @@ concept PtrCastable = requires(const volatile X* x, const volatile Y* y)
static_cast<const volatile Y*>(x); static_cast<const volatile Y*>(x);
static_cast<const volatile X*>(y); static_cast<const volatile X*>(y);
}; };
template <typename X, typename Y> requires PtrCastable<X, Y>
constexpr bool is_same_ptr()
{
if constexpr (std::is_void_v<X> || std::is_void_v<Y> || std::is_same_v<std::remove_cv_t<X>, std::remove_cv_t<Y>>)
{
return true;
}
else if constexpr (sizeof(X) == sizeof(Y))
{
return true;
}
else
{
if (std::is_constant_evaluated())
{
bool result = false;
if constexpr (sizeof(X) < sizeof(Y))
{
std::allocator<Y> a{};
Y* ptr = a.allocate(1);
result = static_cast<X*>(ptr) == static_cast<void*>(ptr);
a.deallocate(ptr, 1);
}
else
{
std::allocator<X> a{};
X* ptr = a.allocate(1);
result = static_cast<Y*>(ptr) == static_cast<void*>(ptr);
a.deallocate(ptr, 1);
}
return result;
}
else
{
std::aligned_union_t<0, X, Y> s;
Y* ptr = reinterpret_cast<Y*>(&s);
return static_cast<X*>(ptr) == static_cast<void*>(ptr);
}
}
}
template <typename X, typename Y> requires PtrCastable<X, Y>
constexpr bool is_same_ptr(const volatile Y* ptr)
{
return static_cast<const volatile X*>(ptr) == static_cast<const volatile void*>(ptr);
}
template <typename X, typename Y>
concept PtrSame = (is_same_ptr<X, Y>());