求可快速实现自定义类型序列化反序列化的C++反射库
满足需求的C++反射方案推荐与实现示例
核心思路
要实现类似Unity的组件序列化,需具备两个核心能力:
- 运行时类型实例化:根据字符串(如"Mesh")创建对应具体类的实例,而非基类指针
- 属性遍历与自动赋值:能获取类的所有属性,读取YAML节点值并自动转换类型赋值
以下是几种无需大量代码即可实现的方案,结合YAML-CPP完成你的需求:
方案1:Boost.PFR + 工厂模式(编译时反射,轻量无冗余)
Boost.PFR是轻量编译时反射库,无需额外宏定义即可遍历结构体/类的公共成员,配合工厂模式实现运行时实例化。
步骤1:组件定义与工厂注册
#include <boost/pfr.hpp> #include <yaml-cpp/yaml.h> #include <unordered_map> #include <memory> // 组件基类 struct IComponent { virtual ~IComponent() = default; virtual void deserialize(const YAML::Node& node) = 0; }; // Mesh组件实现 struct Mesh : IComponent { std::vector<float> Vertices; std::vector<int> Indices; std::string Path; void deserialize(const YAML::Node& node) override { // 遍历所有成员自动赋值 boost::pfr::for_each_field(*this, [&](auto& field, auto idx) { constexpr auto propName = boost::pfr::get_name<idx>(*this); if (node[propName]) { field = node[propName].as<std::decay_t<decltype(field)>>(); } }); } }; // 组件工厂 class ComponentFactory { public: using Creator = std::unique_ptr<IComponent>(*)(); static void registerComponent(const std::string& name, Creator creator) { getCreators()[name] = creator; } static std::unique_ptr<IComponent> create(const std::string& name) { auto it = getCreators().find(name); return it != getCreators().end() ? it->second() : nullptr; } private: static std::unordered_map<std::string, Creator>& getCreators() { static std::unordered_map<std::string, Creator> creators; return creators; } }; // 注册组件的宏 #define REGISTER_COMPONENT(T) \ struct Register##T { \ Register##T() { \ ComponentFactory::registerComponent(#T, []() { return std::make_unique<T>(); }); \ } \ }; \ static Register##T register_##T; REGISTER_COMPONENT(Mesh)
步骤2:反序列化实现(对应你的伪代码)
int main() { YAML::Node node = YAML::LoadFile("scene.yaml"); std::string componentName = node["Scene Object"]["Component"].as<std::string>(); // 生成具体Mesh实例而非IComponent auto component = ComponentFactory::create(componentName); if (!component) { // 处理类型不存在的异常 return 1; } // 自动完成属性赋值 component->deserialize(node["Scene Object"]["Component"]); // 如需访问具体类型成员,用dynamic_cast转换 if (auto mesh = dynamic_cast<Mesh*>(component.get())) { // 操作mesh->Vertices、mesh->Path等成员 } return 0; }
方案2:C++20标准反射(无第三方依赖,需最新编译器)
C++20引入的标准反射(GCC 13+/Clang 16+支持),无需第三方库即可实现反射功能。
实现示例
#include <reflect> #include <yaml-cpp/yaml.h> #include <unordered_map> #include <memory> struct IComponent { virtual ~IComponent() = default; virtual void deserialize(const YAML::Node& node) = 0; }; struct Mesh : IComponent { std::vector<float> Vertices; std::vector<int> Indices; std::string Path; void deserialize(const YAML::Node& node) override { using namespace std::reflect; // 遍历类的所有公共成员 for_each(get_members<Mesh>(), [&](auto member) { std::string propName = get_name(member); if (node[propName]) { get(member, *this) = node[propName].as<decltype(get(member, *this))>(); } }); } }; // 工厂注册部分同方案1,省略重复代码 REGISTER_COMPONENT(Mesh) int main() { YAML::Node node = YAML::LoadFile("scene.yaml"); std::string componentName = node["Scene Object"]["Component"].as<std::string>(); auto component = ComponentFactory::create(componentName); if (component) { component->deserialize(node["Scene Object"]["Component"]); } return 0; }
方案3:自定义轻量反射宏(完全可控,无第三方依赖)
如果不想依赖任何外部库,可以自己实现简单的反射宏,手动注册类的属性信息。
实现示例
#include <yaml-cpp/yaml.h> #include <unordered_map> #include <memory> #include <vector> #include <functional> struct IComponent { virtual ~IComponent() = default; virtual void deserialize(const YAML::Node& node) = 0; }; // 属性反射信息结构体 struct PropertyInfo { std::string name; std::function<void(IComponent*, const YAML::Node&)> setter; }; // 组件元数据 struct ComponentMeta { std::function<std::unique_ptr<IComponent>()> creator; std::vector<PropertyInfo> properties; }; // 全局元数据注册表 std::unordered_map<std::string, ComponentMeta>& getComponentMeta() { static std::unordered_map<std::string, ComponentMeta> meta; return meta; } // 自定义反射宏 #define BEGIN_COMPONENT(T) \ struct T : IComponent { \ void deserialize(const YAML::Node& node) override { \ auto& meta = getComponentMeta()[#T]; \ for (auto& prop : meta.properties) { \ if (node[prop.name]) { \ prop.setter(this, node[prop.name]); \ } \ } \ } #define REGISTER_PROPERTY(T, Type, Prop) \ Type Prop; \ static void set##Prop(IComponent* comp, const YAML::Node& node) { \ static_cast<T*>(comp)->Prop = node.as<Type>(); \ } #define END_COMPONENT(T) \ }; \ struct Register##T { \ Register##T() { \ getComponentMeta()[#T] = { \ []() { return std::make_unique<T>(); }, \ { \ {"Vertices", setVertices}, \ {"Indices", setIndices}, \ {"Path", setPath} \ } \ }; \ } \ }; \ static Register##T register_##T; // 使用宏定义Mesh组件 BEGIN_COMPONENT(Mesh) REGISTER_PROPERTY(Mesh, std::vector<float>, Vertices) REGISTER_PROPERTY(Mesh, std::vector<int>, Indices) REGISTER_PROPERTY(Mesh, std::string, Path) END_COMPONENT(Mesh) // 生成组件的函数 std::unique_ptr<IComponent> generateComponentFromComponentName(const std::string& name) { auto it = getComponentMeta().find(name); return it != getComponentMeta().end() ? it->second.creator() : nullptr; } int main() { YAML::Node node = YAML::LoadFile("scene.yaml"); std::string componentName = node["Scene Object"]["Component"].as<std::string>(); auto component = generateComponentFromComponentName(componentName); if (component) { component->deserialize(node["Scene Object"]["Component"]); } return 0; }
选择建议
- 若使用C++20且编译器支持标准反射,优先选方案2,符合标准且无第三方依赖。
- 若需兼容旧C++标准,方案1的Boost.PFR最省心,无需手动维护属性列表,编译时反射性能优异。
- 若完全不想依赖外部库,方案3的自定义宏可完全控制反射逻辑,但需手动维护属性注册列表。
内容的提问来源于stack exchange,提问作者Davide Paolillo
相关产品推荐
相关产品推荐

