mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-25 04:02:42 +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:
parent
eec9578619
commit
f5e529db61
@ -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(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>());
|
||||
}
|
||||
|
@ -6,63 +6,8 @@
|
||||
|
||||
namespace stx
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
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 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>
|
||||
class single_ptr;
|
||||
@ -175,12 +120,9 @@ namespace stx
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -200,12 +142,9 @@ namespace stx
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -269,12 +208,9 @@ 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>() != same_ptr::no>>
|
||||
template <typename U> requires PtrSame<T, U>
|
||||
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;
|
||||
@ -436,12 +372,9 @@ namespace stx
|
||||
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
|
||||
{
|
||||
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++;
|
||||
@ -453,22 +386,16 @@ namespace stx
|
||||
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
|
||||
{
|
||||
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> != same_ptr::no>>
|
||||
template <typename U> requires same_ptr_implicit_v<T, U>
|
||||
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;
|
||||
r.m_ptr = nullptr;
|
||||
}
|
||||
@ -486,12 +413,9 @@ namespace stx
|
||||
|
||||
[[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
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -502,22 +426,16 @@ namespace stx
|
||||
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
|
||||
{
|
||||
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> != same_ptr::no>>
|
||||
template <typename U> requires same_ptr_implicit_v<T, U>
|
||||
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;
|
||||
}
|
||||
@ -535,12 +453,9 @@ 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>() != same_ptr::no>>
|
||||
template <typename U> requires PtrSame<T, U>
|
||||
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)
|
||||
@ -618,12 +533,9 @@ 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>() != same_ptr::no>>
|
||||
template <typename U> requires PtrSame<T, U>
|
||||
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++;
|
||||
@ -635,12 +547,9 @@ 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>() != same_ptr::no>>
|
||||
template <typename U> requires PtrSame<T, U>
|
||||
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;
|
||||
@ -702,24 +611,18 @@ namespace stx
|
||||
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
|
||||
{
|
||||
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> != same_ptr::no>>
|
||||
template <typename U> requires same_ptr_implicit_v<T, U>
|
||||
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;
|
||||
r.m_ptr = nullptr;
|
||||
|
||||
@ -727,12 +630,9 @@ namespace stx
|
||||
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
|
||||
{
|
||||
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;
|
||||
|
||||
@ -762,32 +662,23 @@ namespace stx
|
||||
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
|
||||
{
|
||||
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> != same_ptr::no>>
|
||||
template <typename U> requires same_ptr_implicit_v<T, U>
|
||||
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> != same_ptr::no>>
|
||||
template <typename U> requires same_ptr_implicit_v<T, U>
|
||||
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;
|
||||
}
|
||||
@ -1048,24 +939,18 @@ namespace stx
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
|
||||
ensure(is_same_ptr_test<T, U>(cmp.m_ptr));
|
||||
|
||||
shared_type old = cmp;
|
||||
static_cast<void>(compare_exchange(old, std::move(exch)));
|
||||
return old;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
|
||||
@ -1102,24 +987,18 @@ namespace stx
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
if constexpr (is_same_ptr<T, U>() == same_ptr::maybe)
|
||||
ensure(is_same_ptr_test<T, U>(cmp.m_ptr));
|
||||
|
||||
shared_type old = cmp;
|
||||
static_cast<void>(compare_exchange(old, std::move(exch)));
|
||||
return old;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
@ -1169,13 +1048,13 @@ namespace stx
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
return observe() == r.get();
|
||||
|
@ -250,7 +250,7 @@ namespace stx
|
||||
else
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -269,7 +269,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>() == same_ptr::yes); // TODO
|
||||
static_assert(PtrSame<T, As>); // TODO
|
||||
|
||||
return type_counter<Info>::template dyn_type<T, As>;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
#include <compare>
|
||||
#include <memory>
|
||||
#include <bit>
|
||||
|
||||
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 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>());
|
||||
|
Loading…
Reference in New Issue
Block a user