// header file extern Foo g_foo; // cpp file Foo g_foo;
void PrepareFoo(...) { FooManager::getInstance().Initialize (); }
void Draw(Renderer& render_instance)
method void Draw(Renderer& render_instance)
, rather than using the global Render::Instance
(). More examples and rationales why you should not use singleton can be read in the post .SSystemGlobalEnvironment
). Global objects are wrapped in one structure and are pointers to abstract entities that are initialized at the right time in the right place in the program. No extra overhead, no extra add-ons, type control during compilation - beauty!GlobalObjectBase
. Using the shell will add operations at runtime, so be sure to pay attention to performance after changes. class GlobalObjectBase { public: virtual ~GlobalObjectBase() {} };
class GlobalObjectsStorage { private: using ObjPtr = std::unique_ptr<GlobalObjectBase>; std::vector<ObjPtr> m_dynamic_globals; private: GlobalObjectBase* GetGlobalObjectImpl(size_t i_type_code) const { … } void AddGlobalObjectImpl(std::unique_ptr<GlobalObjectBase> ip_object) { … } void RemoveGlobalObjectImpl(size_t i_type_code) { … } public: GlobalObjectsStorage() {} template <typename ObjectType> void AddGlobalObject() { AddGlobalObjectImpl(std::make_unique<ObjectType>()); } template <typename ObjectType> ObjectType* GetGlobalObject() const { return static_cast<ObjectType*>(GetGlobalObjectImpl(typeid(ObjectType).hash_code()); } template <typename ObjectType> void RemoveGlobalObject() { RemoveGlobalObjectImpl(typeid(ObjectType).hash_code()); } };
GlobalObjectsStorage
interface GlobalObjectsStorage
methods that transfer the necessary implementation data. class FooManager : public GlobalObjectBase { public: void Initialize() {} }; static GlobalObjectsStorage g_storage; // void Test() { // "" g_storage.AddGlobalObject<FooManager>(); // g_storage.GetGlobalObject<FooManager>()->Initialize(); // g_storage.RemoveGlobalObject<FooManager>(); }
template <typename BaseObject> class ObjectStorageBase { private: virtual BaseObject* GetGlobalObjectImpl(size_t i_type_code) const = 0; virtual void AddGlobalObjectImpl(std::unique_ptr<BaseObject> ip_object) = 0; virtual void RemoveGlobalObjectImpl(size_t i_type_code) = 0; public: virtual ~ObjectStorageBase() {} template <typename ObjectType> void AddGlobalObject() { AddGlobalObjectImpl(std::make_unique<ObjectType>()); } template <typename ObjectType> ObjectType* GetGlobalObject() const { return static_cast<ObjectType*>(GetGlobalObjectImpl(typeid(ObjectType).hash_code())); } template <typename ObjectType> void RemoveGlobalObject() { RemoveGlobalObjectImpl(typeid(ObjectType).hash_code()); } virtual std::vector<BaseObject*> GetStoredObjects() = 0; }; class GameGlobalObject : public GlobalObjectBase { public: virtual ~GameGlobalObject() {} virtual void Update(float dt) {} virtual void Init() {} virtual void Release() {} }; class DefaultObjectsStorage : public ObjectStorageBase<GameGlobalObject> { private: using ObjPtr = std::unique_ptr<GameGlobalObject>; std::vector<ObjPtr> m_dynamic_globals; private: virtual GameGlobalObject* GetGlobalObjectImpl(size_t i_type_code) const override { … } virtual void AddGlobalObjectImpl(std::unique_ptr<GameGlobalObject> ip_object) override { … } virtual void RemoveGlobalObjectImpl(size_t i_type_code) override { … } public: DefaultObjectsStorage() {} virtual std::vector<GameGlobalObject*> GetStoredObjects() override { return m_cache_objects; } }; static std::unique_ptr<ObjectStorageBase<GameGlobalObject>> gp_storage(new DefaultObjectsStorage()); void Test() { // "" gp_storage->AddGlobalObject<ResourceManager>(); // gp_storage->GetGlobalObject<ResourceManager>()->Initialize(); // gp_storage->RemoveGlobalObject<ResourceManager>(); }
for (auto p_object : g_storage->GetStoredObjects()) p_object->Init();
DefaultObjectsStorage
. Result for 1,000,000 calls.typeid(*obj).hash_code()
takes the most time. Since the extraction of data about the types during execution spends a lot of CPU time, you need to get around it. The easiest way to do this is to store a type hash in the base global object class ( GlobalObjectBase
). class GlobalObjectBase { protected: size_t m_hash_code; public: ... size_t GetTypeHashCode() const { return m_hash_code; } virtual void RecalcHashCode() { m_hash_code = typeid(*this).hash_code(); } };
ObjectStorageBase::AddGlobalObject DefaultObjectsStorage:: GetGlobalObjectImpl
. Additionally, we statically store type data in the template function of the parent class ObjectStorageBase::GetGlobalObject
. template <typename BaseObject> class ObjectStorageBase { … public: template <typename ObjectType> void AddGlobalObject() { auto p_object = std::make_unique<ObjectType>(); p_object->RecalcHashCode(); AddGlobalObjectImpl(std::move(p_object)); } template <typename ObjectType> ObjectType* GetGlobalObject() const { static size_t type_hash = typeid(ObjectType).hash_code()); return static_cast<ObjectType*>(GetGlobalObjectImpl(type_hash); } … }; class DefaultObjectsStorage : public ObjectStorageBase<GameGlobalObject> { … private: virtual GlobalObjectBase* GetGlobalObjectImpl(size_t i_type_code) const override { auto it = std::find_if(m_dynamic_globals.begin(), m_dynamic_globals.end(), [i_type_code](const ObjPtr& obj) { return obj->GetTypeHashCode() == i_type_code; }); if (it == m_dynamic_globals.end()) { // , - return nullptr; } return it->get(); } … };
GlobalObjectBase::RecalcHashCode
and selectively replace only the necessary objects. To replace the main class, it is necessary to make the methods necessary for the test and the test class inheritor virtual. struct Foo : public GlobalObjectBase { int x = 0; virtual void SetX() { x = rand()%1; } }; struct FooTest : public Foo { virtual void SetX() override { x = 5; } virtual void RecalcHashCode() { m_hash_code = typeid(First).hash_code(); } }; g_getter.AddGlobalObject<FooTest>(); g_getter.GetGlobalObject<Foo>()->SetX();
Source: https://habr.com/ru/post/316416/
All Articles