#pragma once #include "Utilities/SharedMutex.h" #define ID_MANAGER_INCLUDED // TODO: make id_aux_initialize and id_aux_finalize safer against a possible ODR violation // Function called after the successfull creation of an ID (does nothing by default, provide an overload) inline void id_aux_initialize(void*) { ; } // Function called after the ID removal (does nothing by default, provide an overload) inline void id_aux_finalize(void*) { ; } // Type-erased id_aux_* function type using id_aux_func_t = void(*)(void*); template struct id_type_info_t { static const auto size = sizeof(T); // forbid forward declarations static const id_aux_func_t on_remove; }; // Type-erased finalization function template const id_aux_func_t id_type_info_t::on_remove = [](void* ptr) { return id_aux_finalize(static_cast(ptr)); }; using id_type_index_t = const id_aux_func_t*; // Get a unique pointer to the on_remove value (will be unique for each type) template inline constexpr id_type_index_t get_id_type_index() { return &id_type_info_t::on_remove; } // Default ID traits for any arbitrary type template struct id_traits { static const auto size = sizeof(T); // forbid forward declarations // 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; } }; // ID Manager // 0 is invalid ID // 1..0x7fffffff : general purpose IDs // 0x80000000+ : reserved (may be used through id_traits specializations) namespace idm { struct id_data_t final { std::shared_ptr data; const std::type_info* info; id_type_index_t type_index; template id_data_t(const std::shared_ptr& data) : data(data) , info(&typeid(T)) , type_index(get_id_type_index()) { } }; // Custom hasher for ID values (map to itself) struct id_hash_t final { std::size_t operator ()(u32 value) const { return value; } }; using map_t = std::unordered_map; // Can be called from the constructor called through make() or make_ptr() to get the ID of the object being created inline u32 get_last_id() { extern thread_local u32 g_tls_last_id; return g_tls_last_id; } // Remove all objects void clear(); // Internal bool check(u32 in_id, id_type_index_t type); // Check if an ID of specified type exists template bool check(u32 id) { return check(id_traits::in_id(id), get_id_type_index()); } // Check if an ID exists and return its type or nullptr const std::type_info* get_type(u32 raw_id); // Internal template std::shared_ptr add(Ptr&& get_ptr) { extern shared_mutex g_mutex; extern idm::map_t g_map; extern u32 g_last_raw_id; extern thread_local u32 g_tls_last_id; 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); std::shared_ptr ptr = get_ptr(); g_map.emplace(raw_id, id_data_t(ptr)); if (raw_id < 0x80000000) g_last_raw_id = raw_id; return ptr; } return nullptr; } // Add a new ID of specified type with specified constructor arguments (returns object or nullptr) template std::enable_if_t::value, std::shared_ptr> make_ptr(Args&&... args) { if (auto ptr = add(WRAP_EXPR(std::make_shared(std::forward(args)...)))) { id_aux_initialize(ptr.get()); return ptr; } return nullptr; } // Add a new ID of specified type with specified constructor arguments (returns id) template std::enable_if_t::value, u32> make(Args&&... args) { if (auto ptr = add(WRAP_EXPR(std::make_shared(std::forward(args)...)))) { id_aux_initialize(ptr.get()); return get_last_id(); } throw EXCEPTION("Out of IDs ('%s')", typeid(T).name()); } // Add a new ID for an existing object provided (returns new id) template u32 import(const std::shared_ptr& ptr) { static const auto size = sizeof(T); // forbid forward declarations if (add(WRAP_EXPR(ptr))) { id_aux_initialize(ptr.get()); return get_last_id(); } throw EXCEPTION("Out of IDs ('%s')", typeid(T).name()); } // Internal std::shared_ptr get(u32 in_id, id_type_index_t type); // Get ID of specified type template std::shared_ptr get(u32 id) { return std::static_pointer_cast(get(id_traits::in_id(id), get_id_type_index())); } // Internal idm::map_t get_all(id_type_index_t type); // Get all IDs of specified type T (unsorted) template std::vector> get_all() { std::vector> result; for (auto& id : get_all(get_id_type_index())) { result.emplace_back(std::static_pointer_cast(id.second.data)); } return result; } std::shared_ptr withdraw(u32 in_id, id_type_index_t type); // Remove the ID created with type T template bool remove(u32 id) { if (auto ptr = withdraw(id_traits::in_id(id), get_id_type_index())) { id_aux_finalize(static_cast(ptr.get())); return true; } return false; } // Remove the ID created with type T and return it template std::shared_ptr withdraw(u32 id) { if (auto ptr = std::static_pointer_cast(withdraw(id_traits::in_id(id), get_id_type_index()))) { id_aux_finalize(ptr.get()); return ptr; } return nullptr; } u32 get_count(id_type_index_t type); template u32 get_count() { return get_count(get_id_type_index()); } // Get sorted list of all IDs of specified type template std::set get_set() { std::set result; for (auto& id : get_all(get_id_type_index())) { result.emplace(id_traits::out_id(id.first)); } return result; } // Get sorted map (ID value -> ID data) of all IDs of specified type template std::map> get_map() { std::map> result; for (auto& id : get_all(get_id_type_index())) { result[id_traits::out_id(id.first)] = std::static_pointer_cast(id.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 { // Custom hasher for aligned pointer values struct hash_t final { std::size_t operator()(id_type_index_t value) const { return reinterpret_cast(value) >> 3; } }; using map_t = std::unordered_map, hash_t>; // Remove all objects void clear(); // Internal (returns old and new pointers) template std::pair, std::shared_ptr> add(Ptr&& get_ptr) { extern shared_mutex g_mutex; extern fxm::map_t g_map; std::lock_guard lock(g_mutex); auto& item = g_map[get_id_type_index()]; if (Always || !item) { std::shared_ptr old = std::static_pointer_cast(std::move(item)); std::shared_ptr ptr = get_ptr(); // Set new object item = ptr; return{ std::move(old), std::move(ptr) }; } else { return{ std::static_pointer_cast(item), nullptr }; } } // Create the object (returns nullptr if it already exists) template std::enable_if_t::value, std::shared_ptr> make(Args&&... args) { auto pair = add(WRAP_EXPR(std::make_shared(std::forward(args)...))); if (pair.second) { id_aux_initialize(pair.second.get()); } return std::move(pair.second); } // Create the object unconditionally (old object will be removed if it exists) template std::enable_if_t::value, std::shared_ptr> make_always(Args&&... args) { auto pair = add(WRAP_EXPR(std::make_shared(std::forward(args)...))); if (pair.first) { id_aux_finalize(pair.first.get()); } id_aux_initialize(pair.second.get()); return std::move(pair.second); } // Emplace the object returned by provider() and return it if no object exists template auto import(F&& provider) -> decltype(static_cast>(provider())) { static const auto size = sizeof(T); // forbid forward declarations auto pair = add(std::forward(provider)); if (pair.second) { id_aux_initialize(pair.second.get()); } return std::move(pair.second); } // Emplace the object return by provider() (old object will be removed if it exists) template auto import_always(F&& provider) -> decltype(static_cast>(provider())) { static const auto size = sizeof(T); // forbid forward declarations auto pair = add(std::forward(provider)); if (pair.first) { id_aux_finalize(pair.first.get()); } id_aux_initialize(pair.second.get()); return std::move(pair.second); } // Get the object unconditionally (create an object if it doesn't exist) template std::enable_if_t::value, std::shared_ptr> get_always(Args&&... args) { auto pair = add(WRAP_EXPR(std::make_shared(std::forward(args)...))); if (pair.second) { id_aux_initialize(pair.second.get()); return std::move(pair.second); } return std::move(pair.first); } // Internal bool check(id_type_index_t type); // Check whether the object exists template bool check() { return check(get_id_type_index()); } // Internal std::shared_ptr get(id_type_index_t type); // Get the object (returns nullptr if it doesn't exist) template std::shared_ptr get() { return std::static_pointer_cast(get(get_id_type_index())); } // Internal std::shared_ptr withdraw(id_type_index_t type); // Delete the object template bool remove() { if (auto ptr = withdraw(get_id_type_index())) { id_aux_finalize(static_cast(ptr.get())); return true; } return false; } // Delete the object and return it template std::shared_ptr withdraw() { if (auto ptr = std::static_pointer_cast(withdraw(get_id_type_index()))) { id_aux_finalize(ptr.get()); return ptr; } return nullptr; } }