Implement utils::refptr for typemap (with && syntax)

Ref-counted pointer, object is always allocated on heap.
Rvalue reference && in typemap is converted to refptr for convenience.
This commit is contained in:
Nekotekina 2019-02-01 23:29:58 +03:00
parent 6c5d9fffaa
commit 7b344b7654

View File

@ -353,6 +353,121 @@ namespace utils
}
}
// An object of type T paired with atomic refcounter
template <typename T>
class refctr final
{
atomic_t<std::size_t> m_ref{1};
public:
T object;
template <typename... Args>
refctr(Args&&... args)
: object(std::forward<Args>(args)...)
{
}
void add_ref() noexcept
{
m_ref++;
}
std::size_t remove_ref() noexcept
{
return --m_ref;
}
};
// Simplified "shared" ptr making use of refctr<T> class
template <typename T>
class refptr final
{
refctr<T>* m_ptr = nullptr;
void destroy()
{
if (m_ptr && !m_ptr->remove_ref())
delete m_ptr;
}
public:
constexpr refptr() = default;
// Construct directly from refctr<T> pointer
explicit refptr(refctr<T>* ptr) noexcept
: m_ptr(ptr)
{
}
refptr(const refptr& rhs) noexcept
: m_ptr(rhs.m_ptr)
{
if (m_ptr)
m_ptr->add_ref();
}
refptr(refptr&& rhs) noexcept
: m_ptr(rhs.m_ptr)
{
rhs.m_ptr = nullptr;
}
~refptr()
{
destroy();
}
refptr& operator =(const refptr& rhs) noexcept
{
destroy();
m_ptr = rhs.m_ptr;
if (m_ptr)
m_ptr->add_ref();
}
refptr& operator =(refptr&& rhs) noexcept
{
std::swap(m_ptr, rhs.m_ptr);
}
void reset() noexcept
{
destroy();
m_ptr = nullptr;
}
refctr<T>* release() noexcept
{
return std::exchange(m_ptr, nullptr);
}
void swap(refptr&& rhs) noexcept
{
std::swap(m_ptr, rhs.m_ptr);
}
refctr<T>* get() const noexcept
{
return m_ptr;
}
T& operator *() const noexcept
{
return m_ptr->object;
}
T* operator ->() const noexcept
{
return &m_ptr->object;
}
explicit operator bool() const noexcept
{
return !!m_ptr;
}
};
// Internal, typemap control block for a particular type
struct alignas(64) typemap_head
{
@ -1168,44 +1283,48 @@ namespace utils
return true;
}
// Transform T&& into refptr<T>, moving const qualifier from T to refptr<T>
template <typename T, typename U = std::remove_reference_t<T>>
using decode_t = std::conditional_t<!std::is_rvalue_reference_v<T>, T,
std::conditional_t<std::is_const_v<U>, const refptr<std::remove_const_t<U>>, refptr<U>>>;
public:
// Lock any objects by their identifiers, special tags id_new/id_any/id_always, or search predicates
template <typename... Types, typename... Args, typename = std::enable_if_t<sizeof...(Types) == sizeof...(Args)>>
auto lock(Args&&... ids) const
{
static_assert(((!std::is_lvalue_reference_v<Types> == !typeinfo_poly<Types>::is_poly) && ...));
static_assert(((!std::is_rvalue_reference_v<Types>) && ...));
static_assert(((!std::is_array_v<Types>) && ...));
static_assert(((!std::is_void_v<Types>) && ...));
// Initialize pointers
std::array<typeptr_base, sizeof...(Types)> result{this->init_ptr<Types>(std::forward<Args>(ids))...};
std::array<typeptr_base, sizeof...(Types)> result{this->init_ptr<decode_t<Types>>(std::forward<Args>(ids))...};
// Whether requires locking after init_ptr
using locks_t = std::integer_sequence<bool, does_need_lock<Types, Args>()...>;
using locks_t = std::integer_sequence<bool, does_need_lock<decode_t<Types>, Args>()...>;
// Array index helper
using seq_t = std::index_sequence_for<Types...>;
using seq_t = std::index_sequence_for<decode_t<Types>...>;
// Lock any number of objects in safe manner
while (true)
{
const uint locked = lock_array<Types...>(result, seq_t{}, locks_t{});
if (LIKELY(try_lock<0, Types...>(result, locked, locks_t{})))
const uint locked = lock_array<decode_t<Types>...>(result, seq_t{}, locks_t{});
if (LIKELY(try_lock<0, decode_t<Types>...>(result, locked, locks_t{})))
break;
}
// Verify object types
check_array<Types...>(result, seq_t{}, std::forward<Args>(ids)...);
check_array<decode_t<Types>...>(result, seq_t{}, std::forward<Args>(ids)...);
// Return tuple of possibly locked pointers, or a single pointer
if constexpr (sizeof...(Types) != 1)
{
return array_to_tuple<Types...>(result, seq_t{});
return array_to_tuple<decode_t<Types>...>(result, seq_t{});
}
else
{
return typeptr<Types...>(result[0]);
return typeptr<decode_t<Types>...>(result[0]);
}
}
@ -1214,17 +1333,16 @@ namespace utils
ullong apply(F&& func)
{
static_assert(!std::is_lvalue_reference_v<Type> == !typeinfo_poly<Type>::is_poly);
static_assert(!std::is_rvalue_reference_v<Type>);
static_assert(!std::is_array_v<Type>);
static_assert(!std::is_void_v<Type>);
const uint type_id = g_typeinfo<std::decay_t<Type>>.type;
const uint type_id = g_typeinfo<std::decay_t<decode_t<Type>>>.type;
typemap_head* head = get_head<Type>();
typemap_head* head = get_head<decode_t<Type>>();
const ullong ix = head->m_create_count;
for (std::size_t j = 0; j < (typeinfo_count<Type>::max_count != 1 ? +head->m_limit : 1); j++)
for (std::size_t j = 0; j < (typeinfo_count<decode_t<Type>>::max_count != 1 ? +head->m_limit : 1); j++)
{
const auto block = reinterpret_cast<typemap_block*>(head->m_ptr + j * head->m_ssize);
@ -1240,7 +1358,7 @@ namespace utils
}
else
{
std::invoke(std::forward<F>(func), *block->get_ptr<Type>());
std::invoke(std::forward<F>(func), *block->get_ptr<decode_t<Type>>());
}
}
}