#pragma once #ifdef __cplusplus #include #include #include #include #include #include /* * This class can attach additional data to pointers. It can only attach a single instance of each type of data. * Use the ObjectExtension::Register class to register a type to be used as an object extension. * An example usage is: * * struct MyData { * s32 data = -1; * }; * static ObjectExtension::Register MyDataRegister; * * Then you can get with * ObjectExtension::GetInstance().Get(ptr); * and set with * ObjectExtension::GetInstance().Set(ptr, MyData{}); * (or with the returned pointer from Get()). */ class ObjectExtension { public: using Id = uint32_t; static constexpr Id InvalidId = std::numeric_limits::max(); // Registers type T to be used as an object extension template class Register { public: Register() { Id = ObjectExtension::GetInstance().RegisterId(); } static ObjectExtension::Id Id; }; // Gets the singleton ObjectExtension instance static ObjectExtension& GetInstance(); // Gets the data of type T associated with an object, or nullptr if no such data has been attached template T* Get(const void* object) { assert(ObjectExtension::Register::Id != InvalidId); if (object == nullptr) { return nullptr; } auto it = Data.find(std::make_pair(object, ObjectExtension::Register::Id)); if (it == Data.end()) { return nullptr; } return std::any_cast(&(it->second)); } // Sets the data of type T for an object. Data will be copied. template void Set(const void* object, const T&& data) { assert(ObjectExtension::Register::Id != InvalidId); if (object != nullptr) { Data[std::make_pair(object, ObjectExtension::Register::Id)] = data; } } // Returns true if an object has data of type T associated with it template bool Has(const void* object) { assert(ObjectExtension::Register::Id != InvalidId); if (object == nullptr) { return false; } return Data.contains(std::make_pair(object, ObjectExtension::Register::Id)); } // Removes data of type T from an object template void Remove(const void* object) { assert(ObjectExtension::Register::Id != InvalidId); Data.erase(std::make_pair(object, ObjectExtension::Register::Id)); } // Removes all data from an object void Free(const void* object); private: ObjectExtension() = default; // Returns the next free object extension Id Id RegisterId(); ObjectExtension::Id NextId = 0; struct KeyHash { std::size_t operator()(const std::pair& key) const { return std::hash{}(key.first) ^ (std::hash{}(key.second) << 1); } }; // Collection of all object extension data. std::unordered_map, std::any, KeyHash> Data; }; // Static template globals template ObjectExtension::Id ObjectExtension::Register::Id = ObjectExtension::InvalidId; #else // __cplusplus void ObjectExtension_Free(const void* object); #endif // __cplusplus