本文会展示一个小巧灵活的系统,用于使用C ++ 11语言功能进行运行时反射 。这是一个为C ++类型生成元数据的系统 。元数据采用TypeDescriptor在运行时创建的对象的形式,该对象描述其他运行时对象的结构 。
文章插图
我将这些对象称为类型描述符 。我写这个反射系统的最初动机是为了支持序列化用于一个C ++游戏引擎,因为有非常具体的需求,一旦可行,我也开始将运行时反射用于其他引擎功能:
- 3D渲染:每次游戏引擎使用OpenGL ES进行绘制时,它都会使用反射来传递统一的参数并向API描述顶点格式,这样会使图形编程更加高效!
- 导入JSON:引擎的管道具备通用例程,可以从JSON文件和类型描述符合成C ++对象 。它用于导入3D模型,关卡定义和其他应用 。
为了说明其工作原理,我在GitHub上发布了一个示例项目:(代码贴在文末处)
文章插图
该示例实际上并未使用我的游戏引擎的反射系统 。而是使用了自带的微型反射系统,但类型描述符的创建,结构化和寻找方式几乎相同 。这就是我将在这篇文章中重点讨论的部分 。
本文供那些对如何开发运行时反射系统感兴趣的程序员,而不仅仅是使用涉及C ++的高级功能,但是示例项目只有242行代码,因此希望能够持续迭代,任何C ++程序员都提交代码,如果您对使用现有解决方案更感兴趣,请查看RTTR 。
示例在中main.cpp,示例项目定义了一个名为的结构Node 。该REFLECT()宏告诉系统,以使这种类型的反射 。
struct Node {std::string key;int value;std::vector<Node> children;REFLECT()// 此类型启用反射};
在运行时,该示例创建一个类型为的对象Node 。//创建类型为Node的对象 Node node = {"Apple", 3, {{"banana", 7, {}}, {"cherry", 11, {}}}};
在内存中,Node对象看起来像这样:文章插图
接下来,该示例找到Node的类型描述符 。为此,必须将以下宏放在main.cpp文件中的某个位置 。我将它们放在中Main.cpp,但也可将它们放在Node可见其定义的任何文件中 。
//定义节点的类型描述符REFLECT_STRUCT_BEGIN(Node)REFLECT_STRUCT_MEMBER(key)REFLECT_STRUCT_MEMBER(value)REFLECT_STRUCT_MEMBER(children)REFLECT_STRUCT_END()
Node现在据说可以反映其成员变量 。Node可以通过调用获得指向类型描述符的指针reflect::TypeResolver<Node>::get():
//查找Node的类型描述符reflect::TypeDescriptor* typeDesc = reflect::TypeResolver<Node>::get();
找到类型描述符后,该示例将其用于将Node对象的描述转储到控制台 。//将Node对象的描述转储到控制台typeDesc->dump(&node);
运行的输出为:文章插图
宏的实现方式将REFLECT()宏添加到结构或类时,它会声明两个其他静态成员:Reflection,结构的类型描述符和initReflection初始化它的函数 。实际上,展开宏后,完整的Node结构如下所示:
struct Node {std::string key;int value;std::vector<Node> children;// Declare the struct's type descriptor:static reflect::TypeDescriptor_Struct Reflection;// Declare a function to initialize it:static void initReflection(reflect::TypeDescriptor_Struct*);};
同样,展开后的REFLECT_STRUCT_*()宏块main.cpp如下所示://定义结构类型描述符: reflect::TypeDescriptor_Struct Node::Reflection{Node::initReflection};//初始化它的函数的定义:void Node::initReflection(reflect::TypeDescriptor_Struct* typeDesc) {using T = Node;typeDesc->name = "Node";typeDesc->size = sizeof(T);typeDesc->members = {{"key", offsetof(T, key), reflect::TypeResolver<decltype(T::key)>::get()},{"value", offsetof(T, value), reflect::TypeResolver<decltype(T::value)>::get()},{"children", offsetof(T, children), reflect::TypeResolver<decltype(T::children)>::get()},};}
现在,由于Node::Reflection是静态成员变量,因此initReflection()在程序启动时会自动调用其构造函数,该构造函数接受指向的指针 。您可能想知道:为什么将函数指针传递给构造函数?为什么不通过初始化列表呢?答案是因为函数的主体为我们提供了声明C ++ 11 类型别名的位置:using T = Node 。没有类型别名,所以我们必须将标识符Node作为额外的参数传递给每个REFLECT_STRUCT_MEMBER()宏 。
推荐阅读
- Excel2010条件格式筛选如何使用
- 吃灵芝禁忌人群,枸杞茉莉花茶的宜忌人群都有哪些
- 灵芝与什么起,喝菊花茶有什么好处
- 选词助手免费使用的吗 淘宝免费选词工具
- 打印机的基础使用常识及故障维护 如何使用打印机
- PHP使用反向Ajax技术实现在线客服系统
- AMD|放弃AMD/Intel 俄罗斯超算将使用自研CPU:28nm 8核 ARM
- 灵芝最多可以煮两次,常用的花草茶有哪些
- 就业|被996吓退?88%的00后愿意灵活就业:UP主成最想从事职业
- 使用正肾上腺素需要注意些什么