#pragma once #define ID_MANAGER_INCLUDED template struct type_info_t { static char value; }; template char type_info_t::value = 42; template constexpr inline const void* get_type_index() { return &type_info_t::value; } // default traits for any arbitrary type template struct id_traits { // get next mapped id (may return 0 if out of IDs) static u32 next_id(u32 raw_id) { return raw_id < 0x80000000 ? (raw_id + 1) & 0x7fffffff : 0; } // convert "public" id to mapped id (may return 0 if invalid) static u32 in_id(u32 id) { return id; } // convert mapped id to "public" id static u32 out_id(u32 raw_id) { return raw_id; } }; struct id_data_t final { std::shared_ptr data; const std::type_info* info; const void* type_index; template inline id_data_t(std::shared_ptr data) : data(std::move(data)) , info(&typeid(T)) , type_index(get_type_index()) { } }; // ID Manager // 0 is invalid ID // 1..0x7fffffff : general purpose IDs // 0x80000000+ : reserved (may be used through id_traits specializations) namespace idm { extern std::mutex g_mutex; extern std::unordered_map g_map; extern u32 g_last_raw_id; thread_local extern u32 g_tls_last_id; // can be called from the constructor called through make() or make_ptr() to get the ID of the object being created inline static u32 get_last_id() { return g_tls_last_id; } // reinitialize ID manager static void clear() { std::lock_guard lock(g_mutex); g_map.clear(); g_last_raw_id = 0; } // check if ID of specified type exists template static bool check(u32 id) { std::lock_guard lock(g_mutex); const auto found = g_map.find(id_traits::in_id(id)); return found != g_map.end() && found->second.type_index == get_type_index(); } // check if ID exists and return its type or nullptr inline static const std::type_info* get_type(u32 raw_id) { std::lock_guard lock(g_mutex); const auto found = g_map.find(raw_id); return found == g_map.end() ? nullptr : found->second.info; } // add new ID of specified type with specified constructor arguments (returns object or nullptr) template static std::enable_if_t::value, std::shared_ptr> make_ptr(Args&&... args) { std::lock_guard lock(g_mutex); for (u32 raw_id = g_last_raw_id; (raw_id = id_traits::next_id(raw_id)); /**/) { if (g_map.find(raw_id) != g_map.end()) continue; g_tls_last_id = id_traits::out_id(raw_id); auto ptr = std::make_shared(std::forward(args)...); g_map.emplace(raw_id, id_data_t(ptr)); if (raw_id < 0x80000000) g_last_raw_id = raw_id; return ptr; } return nullptr; } // add new ID of specified type with specified constructor arguments (returns id) template static std::enable_if_t::value, u32> make(Args&&... args) { std::lock_guard lock(g_mutex); for (u32 raw_id = g_last_raw_id; (raw_id = id_traits::next_id(raw_id)); /**/) { if (g_map.find(raw_id) != g_map.end()) continue; g_tls_last_id = id_traits::out_id(raw_id); g_map.emplace(raw_id, id_data_t(std::make_shared(std::forward(args)...))); if (raw_id < 0x80000000) g_last_raw_id = raw_id; return id_traits::out_id(raw_id); } throw EXCEPTION("Out of IDs"); } // add new ID for an existing object provided (don't use for initial object creation) template static u32 import(const std::shared_ptr& ptr) { std::lock_guard lock(g_mutex); for (u32 raw_id = g_last_raw_id; (raw_id = id_traits::next_id(raw_id)); /**/) { if (g_map.find(raw_id) != g_map.end()) continue; g_tls_last_id = id_traits::out_id(raw_id); g_map.emplace(raw_id, id_data_t(ptr)); if (raw_id < 0x80000000) g_last_raw_id = raw_id; return id_traits::out_id(raw_id); } throw EXCEPTION("Out of IDs"); } // get ID of specified type template static std::shared_ptr get(u32 id) { std::lock_guard lock(g_mutex); const auto found = g_map.find(id_traits::in_id(id)); if (found == g_map.end() || found->second.type_index != get_type_index()) { return nullptr; } return std::static_pointer_cast(found->second.data); } // get all IDs of specified type T (unsorted) template static std::vector> get_all() { std::lock_guard lock(g_mutex); std::vector> result; const auto type = get_type_index(); for (auto& v : g_map) { if (v.second.type_index == type) { result.emplace_back(std::static_pointer_cast(v.second.data)); } } return result; } // remove ID created with type T template static bool remove(u32 id) { std::lock_guard lock(g_mutex); const auto found = g_map.find(id_traits::in_id(id)); if (found == g_map.end() || found->second.type_index != get_type_index()) { return false; } g_map.erase(found); return true; } // remove ID created with type T and return the object template static std::shared_ptr withdraw(u32 id) { std::lock_guard lock(g_mutex); const auto found = g_map.find(id_traits::in_id(id)); if (found == g_map.end() || found->second.type_index != get_type_index()) { return nullptr; } auto ptr = std::static_pointer_cast(found->second.data); g_map.erase(found); return ptr; } template static u32 get_count() { std::lock_guard lock(g_mutex); u32 result = 0; const auto type = get_type_index(); for (auto& v : g_map) { if (v.second.type_index == type) { result++; } } return result; } // get sorted ID list of specified type template static std::set get_set() { std::lock_guard lock(g_mutex); std::set result; const auto type = get_type_index(); for (auto& v : g_map) { if (v.second.type_index == type) { result.insert(id_traits::out_id(v.first)); } } return result; } // get sorted ID map (ID value -> ID data) of specified type template static std::map> get_map() { std::lock_guard lock(g_mutex); std::map> result; const auto type = get_type_index(); for (auto& v : g_map) { if (v.second.type_index == type) { result[id_traits::out_id(v.first)] = std::static_pointer_cast(v.second.data); } } return result; } }; // Fixed Object Manager // allows to manage shared objects of any specified type, but only one object per type; // object are deleted when the emulation is stopped namespace fxm { extern std::mutex g_mutex; extern std::unordered_map> g_map; // reinitialize static void clear() { std::lock_guard lock(g_mutex); g_map.clear(); } // add fixed object of specified type only if it doesn't exist (one unique object per type may exist) template static std::enable_if_t::value, std::shared_ptr> make(Args&&... args) { std::lock_guard lock(g_mutex); const auto index = get_type_index(); const auto found = g_map.find(index); // only if object of this type doesn't exist if (found == g_map.end()) { auto ptr = std::make_shared(std::forward(args)...); g_map.emplace(index, ptr); return ptr; } return nullptr; } // add fixed object of specified type, replacing previous one if it exists template static std::enable_if_t::value, std::shared_ptr> make_always(Args&&... args) { std::lock_guard lock(g_mutex); auto ptr = std::make_shared(std::forward(args)...); g_map[get_type_index()] = ptr; return ptr; } // import existing fixed object of specified type only if it doesn't exist (don't use) template static std::shared_ptr import(std::shared_ptr&& ptr) { std::lock_guard lock(g_mutex); const auto index = get_type_index(); const auto found = g_map.find(index); if (found == g_map.end()) { g_map.emplace(index, ptr); return ptr; } return nullptr; } // import existing fixed object of specified type, replacing previous one if it exists (don't use) template static std::shared_ptr import_always(std::shared_ptr&& ptr) { std::lock_guard lock(g_mutex); g_map[get_type_index()] = ptr; return ptr; } // get fixed object of specified type (always returns an object, it's created if it doesn't exist) template static std::enable_if_t::value, std::shared_ptr> get_always(Args&&... args) { std::lock_guard lock(g_mutex); const auto index = get_type_index(); const auto found = g_map.find(index); if (found == g_map.end()) { auto ptr = std::make_shared(std::forward(args)...); g_map[index] = ptr; return ptr; } return std::static_pointer_cast(found->second); } // check whether the object exists template static bool check() { std::lock_guard lock(g_mutex); return g_map.find(get_type_index()) != g_map.end(); } // get fixed object of specified type (returns nullptr if it doesn't exist) template static std::shared_ptr get() { std::lock_guard lock(g_mutex); const auto found = g_map.find(get_type_index()); if (found == g_map.end()) { return nullptr; } return std::static_pointer_cast(found->second); } // remove fixed object created with type T template static bool remove() { std::lock_guard lock(g_mutex); const auto found = g_map.find(get_type_index()); if (found == g_map.end()) { return false; } return g_map.erase(found), true; } // remove fixed object created with type T and return it template static std::shared_ptr withdraw() { std::lock_guard lock(g_mutex); const auto found = g_map.find(get_type_index()); if (found == g_map.end()) { return nullptr; } auto ptr = std::static_pointer_cast(std::move(found->second)); return g_map.erase(found), ptr; } };