|
|
|
@@ -20,40 +20,36 @@ namespace id_manager
|
|
|
|
|
// Common global mutex
|
|
|
|
|
extern shared_mutex g_mutex;
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
constexpr std::pair<u32, u32> get_invl_range()
|
|
|
|
|
{
|
|
|
|
|
return {0, 0};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T> requires requires () { T::id_invl_range; }
|
|
|
|
|
constexpr std::pair<u32, u32> get_invl_range()
|
|
|
|
|
{
|
|
|
|
|
return T::id_invl_range;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
concept IdmCompatible = requires () { T::id_base, T::id_step, T::id_count; };
|
|
|
|
|
|
|
|
|
|
// ID traits
|
|
|
|
|
template <typename T, typename = void>
|
|
|
|
|
template <typename T>
|
|
|
|
|
struct id_traits
|
|
|
|
|
{
|
|
|
|
|
static_assert(sizeof(T) == 0, "ID object must specify: id_base, id_step, id_count");
|
|
|
|
|
static_assert(IdmCompatible<T>, "ID object must specify: id_base, id_step, id_count");
|
|
|
|
|
|
|
|
|
|
static constexpr u32 base = 1; // First ID (N = 0)
|
|
|
|
|
static constexpr u32 step = 1; // Any ID: N * id_step + id_base
|
|
|
|
|
static constexpr u32 count = 65535; // Limit: N < id_count
|
|
|
|
|
static constexpr u32 invalid = 0;
|
|
|
|
|
static constexpr std::pair<u32, u32> invl_range{0, 0};
|
|
|
|
|
};
|
|
|
|
|
enum : u32
|
|
|
|
|
{
|
|
|
|
|
base = T::id_base, // First ID (N = 0)
|
|
|
|
|
step = T::id_step, // Any ID: N * id_setp + id_base
|
|
|
|
|
count = T::id_count, // Limit: N < id_count
|
|
|
|
|
invalid = -+!base, // Invalid ID sample
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename T, typename = void>
|
|
|
|
|
struct invl_range_extract_impl
|
|
|
|
|
{
|
|
|
|
|
static constexpr std::pair<u32, u32> invl_range{0, 0};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
struct invl_range_extract_impl<T, std::void_t<decltype(&T::id_invl_range)>>
|
|
|
|
|
{
|
|
|
|
|
static constexpr std::pair<u32, u32> invl_range = T::id_invl_range;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
struct id_traits<T, std::void_t<decltype(&T::id_base), decltype(&T::id_step), decltype(&T::id_count)>>
|
|
|
|
|
{
|
|
|
|
|
static constexpr u32 base = T::id_base;
|
|
|
|
|
static constexpr u32 step = T::id_step;
|
|
|
|
|
static constexpr u32 count = T::id_count;
|
|
|
|
|
static constexpr u32 invalid = -+!base;
|
|
|
|
|
|
|
|
|
|
static constexpr std::pair<u32, u32> invl_range = invl_range_extract_impl<T>::invl_range;
|
|
|
|
|
static constexpr std::pair<u32, u32> invl_range = get_invl_range<T>();
|
|
|
|
|
|
|
|
|
|
static_assert(count && step && u64{step} * (count - 1) + base < u32{umax} + u64{base != 0 ? 1 : 0}, "ID traits: invalid object range");
|
|
|
|
|
|
|
|
|
@@ -61,19 +57,6 @@ namespace id_manager
|
|
|
|
|
static_assert(!invl_range.second || (u64{invl_range.second} + invl_range.first <= 32 /*....*/ ));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Correct usage testing
|
|
|
|
|
template <typename T, typename T2, typename = void>
|
|
|
|
|
struct id_verify : std::integral_constant<bool, std::is_base_of<T, T2>::value>
|
|
|
|
|
{
|
|
|
|
|
// If common case, T2 shall be derived from or equal to T
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename T, typename T2>
|
|
|
|
|
struct id_verify<T, T2, std::void_t<typename T2::id_type>> : std::integral_constant<bool, std::is_same<T, typename T2::id_type>::value>
|
|
|
|
|
{
|
|
|
|
|
// If T2 contains id_type type, T must be equal to it
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class typeinfo
|
|
|
|
|
{
|
|
|
|
|
// Global variable for each registered type
|
|
|
|
@@ -227,20 +210,6 @@ class idm
|
|
|
|
|
using result_type = R;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename F, typename A1, typename A2>
|
|
|
|
|
struct function_traits<void (F::*)(A1, A2&) const>
|
|
|
|
|
{
|
|
|
|
|
using object_type = A2;
|
|
|
|
|
using void_type = void;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename F, typename A1, typename A2>
|
|
|
|
|
struct function_traits<void (F::*)(A1, A2&)>
|
|
|
|
|
{
|
|
|
|
|
using object_type = A2;
|
|
|
|
|
using void_type = void;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Helper type: pointer + return value propagated
|
|
|
|
|
template <typename T, typename RT>
|
|
|
|
|
struct return_pair
|
|
|
|
@@ -296,7 +265,7 @@ class idm
|
|
|
|
|
template <typename T, typename Type>
|
|
|
|
|
static map_data* find_id(u32 id)
|
|
|
|
|
{
|
|
|
|
|
static_assert(id_manager::id_verify<T, Type>::value, "Invalid ID type combination");
|
|
|
|
|
static_assert(PtrSame<T, Type>, "Invalid ID type combination");
|
|
|
|
|
|
|
|
|
|
const u32 index = get_index<Type>(id);
|
|
|
|
|
|
|
|
|
@@ -332,7 +301,7 @@ class idm
|
|
|
|
|
template <typename T, typename Type, typename F>
|
|
|
|
|
static map_data* create_id(F&& provider)
|
|
|
|
|
{
|
|
|
|
|
static_assert(id_manager::id_verify<T, Type>::value, "Invalid ID type combination");
|
|
|
|
|
static_assert(PtrSame<T, Type>, "Invalid ID type combination");
|
|
|
|
|
|
|
|
|
|
// ID traits
|
|
|
|
|
using traits = id_manager::id_traits<Type>;
|
|
|
|
@@ -373,8 +342,8 @@ public:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add a new ID of specified type with specified constructor arguments (returns object or nullptr)
|
|
|
|
|
template <typename T, typename Make = T, typename... Args>
|
|
|
|
|
static inline std::enable_if_t<std::is_constructible<Make, Args...>::value, std::shared_ptr<Make>> make_ptr(Args&&... args)
|
|
|
|
|
template <typename T, typename Make = T, typename... Args> requires (std::is_constructible_v<Make, Args&&...>)
|
|
|
|
|
static inline std::shared_ptr<Make> make_ptr(Args&&... args)
|
|
|
|
|
{
|
|
|
|
|
if (auto pair = create_id<T, Make>([&] { return std::make_shared<Make>(std::forward<Args>(args)...); }))
|
|
|
|
|
{
|
|
|
|
@@ -385,8 +354,8 @@ public:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add a new ID of specified type with specified constructor arguments (returns id)
|
|
|
|
|
template <typename T, typename Make = T, typename... Args>
|
|
|
|
|
static inline std::enable_if_t<std::is_constructible<Make, Args...>::value, u32> make(Args&&... args)
|
|
|
|
|
template <typename T, typename Make = T, typename... Args> requires (std::is_constructible_v<Make, Args&&...>)
|
|
|
|
|
static inline u32 make(Args&&... args)
|
|
|
|
|
{
|
|
|
|
|
if (auto pair = create_id<T, Make>([&] { return std::make_shared<Make>(std::forward<Args>(args)...); }))
|
|
|
|
|
{
|
|
|
|
@@ -396,20 +365,8 @@ public:
|
|
|
|
|
return id_manager::id_traits<Make>::invalid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add a new ID for an existing object provided (returns new id)
|
|
|
|
|
template <typename T, typename Made = T>
|
|
|
|
|
static inline u32 import_existing(const std::shared_ptr<T>& ptr)
|
|
|
|
|
{
|
|
|
|
|
if (auto pair = create_id<T, Made>([&] { return ptr; }))
|
|
|
|
|
{
|
|
|
|
|
return pair->first;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return id_manager::id_traits<Made>::invalid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add a new ID for an object returned by provider()
|
|
|
|
|
template <typename T, typename Made = T, typename F, typename = std::invoke_result_t<F>>
|
|
|
|
|
template <typename T, typename Made = T, typename F> requires (std::is_invocable_v<F&&>)
|
|
|
|
|
static inline u32 import(F&& provider)
|
|
|
|
|
{
|
|
|
|
|
if (auto pair = create_id<T, Made>(std::forward<F>(provider)))
|
|
|
|
@@ -420,6 +377,13 @@ public:
|
|
|
|
|
return id_manager::id_traits<Made>::invalid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add a new ID for an existing object provided (returns new id)
|
|
|
|
|
template <typename T, typename Made = T>
|
|
|
|
|
static inline u32 import_existing(std::shared_ptr<T> ptr)
|
|
|
|
|
{
|
|
|
|
|
return import<T, Made>([&] { return std::move(ptr); });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Access the ID record without locking (unsafe)
|
|
|
|
|
template <typename T, typename Get = T>
|
|
|
|
|
static inline map_data* find_unlocked(u32 id)
|
|
|
|
@@ -497,14 +461,7 @@ public:
|
|
|
|
|
{
|
|
|
|
|
reader_lock lock(id_manager::g_mutex);
|
|
|
|
|
|
|
|
|
|
const auto found = find_id<T, Get>(id);
|
|
|
|
|
|
|
|
|
|
if (found == nullptr) [[unlikely]]
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return std::static_pointer_cast<Get>(found->second);
|
|
|
|
|
return get_unlocked<T, Get>(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the object, access object under reader lock
|
|
|
|
@@ -533,57 +490,46 @@ public:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static constexpr std::false_type unlocked{};
|
|
|
|
|
|
|
|
|
|
// Access all objects of specified type. Returns the number of objects processed.
|
|
|
|
|
template <typename T, typename Get = T, typename F, typename FT = decltype(&std::decay_t<F>::operator()), typename FRT = typename function_traits<FT>::void_type>
|
|
|
|
|
static inline u32 select(F&& func, int = 0)
|
|
|
|
|
// If function result evaluates to true, stop and return the object and the value.
|
|
|
|
|
template <typename T, typename... Get, typename F, typename Lock = std::true_type>
|
|
|
|
|
static inline auto select(F&& func, Lock = {})
|
|
|
|
|
{
|
|
|
|
|
static_assert(id_manager::id_verify<T, Get>::value, "Invalid ID type combination");
|
|
|
|
|
static_assert((PtrSame<T, Get> && ...), "Invalid ID type combination");
|
|
|
|
|
|
|
|
|
|
reader_lock lock(id_manager::g_mutex);
|
|
|
|
|
std::conditional_t<static_cast<bool>(Lock()), reader_lock, const shared_mutex&> lock(id_manager::g_mutex);
|
|
|
|
|
|
|
|
|
|
u32 result = 0;
|
|
|
|
|
using func_traits = function_traits<decltype(&decltype(std::function(std::declval<F>()))::operator())>;
|
|
|
|
|
using object_type = typename func_traits::object_type;
|
|
|
|
|
using result_type = typename func_traits::result_type;
|
|
|
|
|
|
|
|
|
|
for (auto& id : g_fxo->get<id_manager::id_map<T>>().vec)
|
|
|
|
|
{
|
|
|
|
|
if (id.second)
|
|
|
|
|
{
|
|
|
|
|
if (std::is_same<T, Get>::value || id.first.type() == get_type<Get>())
|
|
|
|
|
{
|
|
|
|
|
func(id.first, *static_cast<typename function_traits<FT>::object_type*>(id.second.get()));
|
|
|
|
|
result++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
static_assert(PtrSame<object_type, T>, "Invalid function argument type combination");
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Access all objects of specified type. If function result evaluates to true, stop and return the object and the value.
|
|
|
|
|
template <typename T, typename Get = T, typename F, typename FT = decltype(&std::decay_t<F>::operator()), typename FRT = typename function_traits<FT>::result_type>
|
|
|
|
|
static inline auto select(F&& func)
|
|
|
|
|
{
|
|
|
|
|
static_assert(id_manager::id_verify<T, Get>::value, "Invalid ID type combination");
|
|
|
|
|
|
|
|
|
|
using object_type = typename function_traits<FT>::object_type;
|
|
|
|
|
using result_type = return_pair<object_type, FRT>;
|
|
|
|
|
|
|
|
|
|
reader_lock lock(id_manager::g_mutex);
|
|
|
|
|
std::conditional_t<std::is_void_v<result_type>, u32, return_pair<object_type, result_type>> result{};
|
|
|
|
|
|
|
|
|
|
for (auto& id : g_fxo->get<id_manager::id_map<T>>().vec)
|
|
|
|
|
{
|
|
|
|
|
if (auto ptr = static_cast<object_type*>(id.second.get()))
|
|
|
|
|
{
|
|
|
|
|
if (std::is_same<T, Get>::value || id.first.type() == get_type<Get>())
|
|
|
|
|
if (sizeof...(Get) == 0 || ((id.first.type() == get_type<Get>()) || ...))
|
|
|
|
|
{
|
|
|
|
|
if (FRT result = func(id.first, *ptr))
|
|
|
|
|
if constexpr (std::is_void_v<result_type>)
|
|
|
|
|
{
|
|
|
|
|
return result_type{{id.second, ptr}, std::move(result)};
|
|
|
|
|
func(id.first, *ptr);
|
|
|
|
|
result++;
|
|
|
|
|
}
|
|
|
|
|
else if ((result.ret = func(id.first, *ptr)))
|
|
|
|
|
{
|
|
|
|
|
result.ptr = {id.second, ptr};
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result_type{nullptr};
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove the ID
|
|
|
|
|