#pragma once #include "Utilities/types.h" #include "Utilities/Macro.h" #include "Utilities/Platform.h" #include "Utilities/SharedMutex.h" #include #include #include // Mostly helper namespace namespace id_manager { // Optional ID traits template struct id_traits { using tag = void; static constexpr u32 min = 1; static constexpr u32 max = 0x7fffffff; }; template struct id_traits> { using tag = typename T::id_base; static constexpr u32 min = T::id_min; static constexpr u32 max = T::id_max; }; // Optional object initialization function (called after ID registration) template struct on_init { static inline void func(T*) { // Forbid forward declarations static constexpr auto size = sizeof(std::conditional_t::value, void*, T>); } }; template struct on_init().on_init())> { static inline void func(T* ptr) { if (ptr) ptr->on_init(); } }; // Optional object finalization function (called after ID removal) template struct on_stop { static inline void func(T*) { // Forbid forward declarations static constexpr auto size = sizeof(std::conditional_t::value, void*, T>); } }; template struct on_stop().on_stop())> { static inline void func(T* ptr) { if (ptr) ptr->on_stop(); } }; class typeinfo { // Global variable for each registered type template struct registered { static const u32 index; }; // Access global type list static std::vector& access(); // Add to the global list static u32 add_type(); public: void(*on_init)(void*) = nullptr; void(*on_stop)(void*) = nullptr; // Get type index template static inline u32 get_index() { return registered::index; } // Register functions template static inline void update() { auto& info = access()[get_index()]; info.on_init = [](void* ptr) { return_ id_manager::on_init::func(static_cast(ptr)); }; info.on_stop = [](void* ptr) { return_ id_manager::on_stop::func(static_cast(ptr)); }; } // Read all registered types static inline const auto& get() { return access(); } }; template const u32 typeinfo::registered::index = typeinfo::add_type(); } // Object manager for emulated process. Multiple objects of specified arbitrary type are given unique IDs. class idm { // Rules for ID allocation: // 0) Individual ID counter may be specified for each type by defining 'using id_base = ...;' // 1) If no id_base specified, void is assumed. // 2) g_id[id_base] indicates next ID allocated in g_map. // 3) g_map[id_base] contains the additional copy of object pointer. // Custom hasher for ID values struct id_hash_t final { std::size_t operator ()(u32 value) const { return value; } }; using map_type = std::unordered_map, id_hash_t>; static shared_mutex g_mutex; // Type Index -> ID -> Object. Use global since only one process is supported atm. static std::vector g_map; // Next ID for each category static std::vector g_id; template static inline u32 get_type() { return id_manager::typeinfo::get_index(); } template static inline u32 get_tag() { return get_type::tag>(); } // Update optional ID storage template static inline auto set_id_value(T* ptr, u32 id) -> decltype(static_cast(std::declval().id)) { ptr->id = id; } static inline void set_id_value(...) { } // Helper template struct function_traits; template struct function_traits { using second_type = A2; using return_type = R; }; // Helper template struct bool_if_void { friend bool operator ,(bool lhs, const bool_if_void&) { return lhs; } operator bool() const { return Value; } }; // Prepares new ID, returns nullptr if out of resources static map_type::pointer allocate_id(u32 tag, u32 min, u32 max); // Deallocate ID, returns object static std::shared_ptr deallocate_id(u32 tag, u32 id); // Allocate new ID and construct it from the provider() template static map_type::pointer create_id(F&& provider) { id_manager::typeinfo::update(); id_manager::typeinfo::update::tag>(); writer_lock lock(g_mutex); if (auto place = allocate_id(get_tag(), id_manager::id_traits::min, id_manager::id_traits::max)) { try { // Get object, store it place->second = provider(); // Update ID value if required set_id_value(static_cast(place->second.get()), place->first); return &*g_map[get_type()].emplace(*place).first; } catch (...) { deallocate_id(get_tag(), place->first); throw; } } return nullptr; } // Get ID (internal) static map_type::pointer find_id(u32 type, u32 id); // Remove ID and return object static std::shared_ptr delete_id(u32 type, u32 tag, u32 id); public: // Initialize object manager static void init(); // Remove all objects static void clear(); // Add a new ID of specified type with specified constructor arguments (returns object or nullptr) template static inline std::enable_if_t::value, std::shared_ptr> make_ptr(Args&&... args) { if (auto pair = create_id(WRAP_EXPR(std::make_shared(std::forward(args)...)))) { id_manager::on_init::func(static_cast(pair->second.get())); id_manager::on_stop::func(nullptr); return{ pair->second, static_cast(pair->second.get()) }; } return nullptr; } // Add a new ID of specified type with specified constructor arguments (returns id) template static inline std::enable_if_t::value, u32> make(Args&&... args) { if (auto pair = create_id(WRAP_EXPR(std::make_shared(std::forward(args)...)))) { id_manager::on_init::func(static_cast(pair->second.get())); id_manager::on_stop::func(nullptr); return pair->first; } throw EXCEPTION("Out of IDs ('%s')", typeid(T).name()); } // Add a new ID for an existing object provided (returns new id) template static inline u32 import_existing(const std::shared_ptr& ptr) { if (auto pair = create_id(WRAP_EXPR(ptr))) { id_manager::on_init::func(static_cast(pair->second.get())); id_manager::on_stop::func(nullptr); return pair->first; } throw EXCEPTION("Out of IDs ('%s')", typeid(T).name()); } // Add a new ID for an object returned by provider() template> static inline std::shared_ptr import(F&& provider) { if (auto pair = create_id(std::forward(provider))) { id_manager::on_init::func(static_cast(pair->second.get())); id_manager::on_stop::func(nullptr); return { pair->second, static_cast(pair->second.get()) }; } return nullptr; } // Check whether the ID exists template static inline bool check(u32 id) { reader_lock lock(g_mutex); return find_id(get_type(), id) != nullptr; } // Get the ID template static inline std::shared_ptr get(u32 id) { reader_lock lock(g_mutex); const auto found = find_id(get_type(), id); if (UNLIKELY(found == nullptr)) { return nullptr; } return{ found->second, static_cast(found->second.get()) }; } // Conditionally get the ID, almost similar to select() but for the single object only. template::second_type> static inline auto get(u32 id, F&& pred) { using result_type = std::conditional_t::return_type>::value, void, std::shared_ptr>; reader_lock lock(g_mutex); const auto found = find_id(get_type(), id); if (UNLIKELY(found == nullptr)) { return static_cast(nullptr); } if (pred(id, *static_cast(found->second.get())), bool_if_void()) { return static_cast(std::static_pointer_cast(found->second)); } return static_cast(nullptr); } // Execute for all IDs (unsorted), may return void. If the result evaluates to true, the loop stops and returns the object. template::second_type> static inline auto select(F&& pred) { using result_type = std::conditional_t::return_type>::value, void, std::shared_ptr>; reader_lock lock(g_mutex); for (u32 type : { get_type()... }) { for (auto& id : g_map[type]) { if (pred(id.first, *static_cast(id.second.get())), bool_if_void()) { return static_cast(std::static_pointer_cast(id.second)); } } } return static_cast(nullptr); } // Get count of objects template static inline u32 get_count() { reader_lock lock(g_mutex); return ::size32(g_map[get_type()]); } // Remove the ID template static inline bool remove(u32 id) { auto&& ptr = delete_id(get_type(), get_tag(), id); if (LIKELY(ptr)) { id_manager::on_stop::func(static_cast(ptr.get())); } return ptr.operator bool(); } // Remove the ID and return it template static inline std::shared_ptr withdraw(u32 id) { auto&& ptr = delete_id(get_type(), get_tag(), id); if (LIKELY(ptr)) { id_manager::on_stop::func(static_cast(ptr.get())); } return{ ptr, static_cast(ptr.get()) }; } // Conditionally remove the ID and return it. template static inline std::shared_ptr withdraw(u32 id, F&& pred) { std::shared_ptr ptr; { writer_lock lock(g_mutex); const auto found = find_id(get_type(), id); if (UNLIKELY(found == nullptr || !pred(id, *static_cast(found->second.get())))) { return nullptr; } ptr = deallocate_id(get_tag(), id); g_map[get_type()].erase(id); } id_manager::on_stop::func(static_cast(ptr.get())); return{ ptr, static_cast(ptr.get()) }; } }; // Object manager for emulated process. One unique object per type, or zero. class fxm { // Type Index -> Object. Use global since only one process is supported atm. static std::vector> g_map; static shared_mutex g_mutex; template static inline u32 get_type() { return id_manager::typeinfo::get_index(); } static std::shared_ptr remove(u32 type); public: // Initialize object manager static void init(); // Remove all objects static void clear(); // Create the object (returns nullptr if it already exists) template static std::enable_if_t::value, std::shared_ptr> make(Args&&... args) { id_manager::typeinfo::update(); std::shared_ptr ptr; { writer_lock lock(g_mutex); if (!g_map[get_type()]) { ptr = std::make_shared(std::forward(args)...); g_map[get_type()] = ptr; } } if (ptr) { id_manager::on_init::func(ptr.get()); id_manager::on_stop::func(nullptr); } return ptr; } // Create the object unconditionally (old object will be removed if it exists) template static std::enable_if_t::value, std::shared_ptr> make_always(Args&&... args) { id_manager::typeinfo::update(); std::shared_ptr ptr; std::shared_ptr old; { writer_lock lock(g_mutex); old = std::move(g_map[get_type()]); ptr = std::make_shared(std::forward(args)...); g_map[get_type()] = ptr; } if (old) { id_manager::on_stop::func(static_cast(old.get())); } id_manager::on_init::func(ptr.get()); return ptr; } // Emplace the object returned by provider() and return it if no object exists template static auto import(F&& provider) -> decltype(static_cast>(provider())) { id_manager::typeinfo::update(); std::shared_ptr ptr; { writer_lock lock(g_mutex); if (!g_map[get_type()]) { ptr = provider(); g_map[get_type()] = ptr; } } if (ptr) { id_manager::on_init::func(ptr.get()); id_manager::on_stop::func(nullptr); } return ptr; } // Emplace the object return by provider() (old object will be removed if it exists) template static auto import_always(F&& provider) -> decltype(static_cast>(provider())) { id_manager::typeinfo::update(); std::shared_ptr ptr; std::shared_ptr old; { writer_lock lock(g_mutex); old = std::move(g_map[get_type()]); ptr = provider(); g_map[get_type()] = ptr; } if (old) { id_manager::on_stop::func(static_cast(old.get())); } id_manager::on_init::func(ptr.get()); return ptr; } // Get the object unconditionally (create an object if it doesn't exist) template static std::enable_if_t::value, std::shared_ptr> get_always(Args&&... args) { id_manager::typeinfo::update(); std::shared_ptr ptr; { writer_lock lock(g_mutex); if (auto& value = g_map[get_type()]) { return{ value, static_cast(value.get()) }; } else { ptr = std::make_shared(std::forward(args)...); g_map[get_type()] = ptr; } } id_manager::on_init::func(ptr.get()); id_manager::on_stop::func(nullptr); return ptr; } // Check whether the object exists template static inline bool check() { reader_lock lock(g_mutex); return g_map[get_type()].operator bool(); } // Get the object (returns nullptr if it doesn't exist) template static inline std::shared_ptr get() { reader_lock lock(g_mutex); auto& ptr = g_map[get_type()]; return{ ptr, static_cast(ptr.get()) }; } // Delete the object template static inline bool remove() { auto&& ptr = remove(get_type()); if (ptr) { id_manager::on_stop::func(static_cast(ptr.get())); } return ptr.operator bool(); } // Delete the object and return it template static inline std::shared_ptr withdraw() { auto&& ptr = remove(get_type()); if (ptr) { id_manager::on_stop::func(static_cast(ptr.get())); } return{ ptr, static_cast(ptr.get()) }; } };