如何将Factory注册表宏放置在头文件的类定义之前?
问题背景
当前项目中的ENGINE_SPAWNABLE宏用于将GameObject子类注册到工厂注册表,但必须放在类定义之后:
#pragma once #include "GameObject.h" class Foo : public GameObject { //... }; ENGINE_SPAWNABLE(Foo);
这种写法在大型头文件或包含多个类的头文件中,宏的位置难以快速定位。希望将宏调整到类定义之前,写法如下:
#pragma once #include "GameObject.h" ENGINE_SPAWNABLE(); class Foo : public GameObject { //... };
但现有宏的实现依赖类的完整定义才能识别类名,需要重新设计宏和注册逻辑。
现有核心代码如下:
#pragma once #include <functional> #include <memory> #include <string> #include <unordered_map> #include "Object.h" // 原宏定义 #define ENGINE_SPAWNABLE(CLASSNAME) static bool CLASSNAME##Registered \ = (GameObjectFactory::GetInstance().GetGameObjectRegistry()[#CLASSNAME] = &GameObjectFactory::SpawnObject<CLASSNAME>, true) class GameObjectFactory { public: static GameObjectFactory& GetInstance() { static GameObjectFactory Instance; return Instance; } template <typename TObject> static std::unique_ptr<Object> SpawnObject() { return std::make_unique<TObject>(); } std::shared_ptr<Object> SpawnGameObjectByName(const std::string& Name); std::unordered_map<std::string, std::function<std::shared_ptr<Object>()>>& GetGameObjectRegistry(); private: std::unordered_map<std::string, std::function<std::unique_ptr<Object>()>> Registry; };
解决方案
方案一:利用静态成员延迟注册(轻量改造)
通过前置声明辅助模板类,将注册逻辑延迟到类定义完成后执行,让宏可以放在类定义之前。
1. 修改宏与添加辅助模板
// 前置声明辅助模板,用于延迟触发注册 template<typename T> struct SpawnableRegistrar; // 调整后的宏,放在类定义之前 #define ENGINE_SPAWNABLE(CLASSNAME) \ class CLASSNAME; \ template<> struct SpawnableRegistrar<CLASSNAME>; \ friend struct SpawnableRegistrar<CLASSNAME>; // 辅助模板实现,负责实际注册逻辑 template<typename T> struct SpawnableRegistrar { static bool Registered; }; template<typename T> bool SpawnableRegistrar<T>::Registered = (GameObjectFactory::GetInstance().GetGameObjectRegistry()[#T] = []() -> std::shared_ptr<Object> { return std::make_shared<T>(); }, true);
2. 类定义中添加触发成员
在类内部添加静态成员,触发辅助模板的初始化:
#pragma once #include "GameObject.h" ENGINE_SPAWNABLE(Foo); class Foo : public GameObject { private: static const bool _spawnableRegistration; }; // 在头文件末尾或对应cpp文件中定义该成员(确保类已完整定义) const bool Foo::_spawnableRegistration = SpawnableRegistrar<Foo>::Registered;
方案二:宏自动注入注册逻辑(简化写法)
进一步封装宏,让它自动完成类的前置声明、注册触发成员的注入,减少手动代码:
// 辅助模板保持不变 template<typename T> struct SpawnableRegistrar { static bool Registered; }; template<typename T> bool SpawnableRegistrar<T>::Registered = (GameObjectFactory::GetInstance().GetGameObjectRegistry()[#T] = []() -> std::shared_ptr<Object> { return std::make_shared<T>(); }, true); // 前置宏:放在类定义之前 #define ENGINE_SPAWNABLE(CLASSNAME) \ class CLASSNAME; \ template<> struct SpawnableRegistrar<CLASSNAME>; \ friend struct SpawnableRegistrar<CLASSNAME>; \ \ class CLASSNAME : public GameObject \ { \ private: \ static const bool _spawnableRegistration; \ public: // 后置宏:放在类定义末尾 #define ENGINE_SPAWNABLE_END \ }; \ const bool CLASSNAME::_spawnableRegistration = SpawnableRegistrar<CLASSNAME>::Registered;
使用示例
#pragma once #include "GameObject.h" ENGINE_SPAWNABLE(Foo) // 类的成员定义 void Update() override {}; ENGINE_SPAWNABLE_END
关键注意事项
- 类型匹配修正:原工厂类中
Registry的函数类型是std::function<std::unique_ptr<Object>()>,但SpawnGameObjectByName返回std::shared_ptr,存在类型不匹配。需统一类型,建议将SpawnObject改为返回std::shared_ptr:template <typename TObject> static std::shared_ptr<Object> SpawnObject() { return std::make_shared<TObject>(); } // 同步修改注册表类型 std::unordered_map<std::string, std::function<std::shared_ptr<Object>()>> Registry; - 初始化顺序:依赖
GameObjectFactory的懒加载单例特性(当前实现已满足),确保注册表在注册逻辑执行前已初始化。 - 类名字符串可靠性:如果使用
typeid(T).name()作为注册表键,不同编译器返回的字符串格式可能不一致;显式使用#CLASSNAME(宏字符串化)更可靠。
内容的提问来源于stack exchange,提问作者user21681526
相关产品推荐
相关产品推荐

