| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> C++知识库 -> UE4 C++反射从入门到原理剖解(UE4 4.26) -> 正文阅读 |
|
[C++知识库]UE4 C++反射从入门到原理剖解(UE4 4.26) |
UE4 C++反射具体应用案例测试反射类ATestActor
下面提供了UE4 C++反射的几种常用用法
UE4 C++反射的实现原理剖解CPP反射总体流程? ?说明一下UE4 CPP反射宏(UClass, UPROPERTY, UFUNCTION)是空宏,最后根本没有参与到C++编译当中,具体见ObjectMacros.h ? UBTUBT全称UnrealBuildTool, 用在建立项目配置,如模块依赖,包含文件目录,设置项目优化配置项等,作用类似于premake, cmake这些东西,就我看到的代码而言,UE4 C++ 反射和UBT几乎没什么关系。UnrealBuildTool.exe文件Engine\Source\Programs\UnrealBuildTool\Development UHTUHT 全称UnrealHeaderTool, UE4 C++反射依赖于 UHT这个工具。UHT用在创建新文件xxx.generated.h 和 xxx.gen.cpp , 其中generated.h 主要是相关函数的声明, gen.cpp是具体反射数据的指定(类元数据, 属性反射数据,函数反射数据等) UnrealHeaderTool.exe文件Engine\Binaries\Win64\ UE4 C++反射原理剖解上面说了C++反射代码的使用以及C++反射流程。下面具体分析反射实现细节。UE4的实现C++反射原理的机制看起来有点复杂,实际很简单,用一句话概括:利用宏标记,用UHT进行字符串分析宏标记来生成对应相应的硬编码CPP代码(xxx.generated.h和xxx.gen.cpp), 在CPP硬编码中手动指定属性反射数据,函数反射数据,类反射数据。 UE4 反射类结构? ?拿UClass(UStruct)为例子, 这里UField* Children存储了一个类所有反射函数数据的节点,UField*是一个链表结构,一个UField*节点就是一个UFunction ?同样的, FField* ChildPerperties存储了一个类所有反射变量数据的节点,FField*是一个链表结构,一个FField*节点就是一个FProperty 宏标记UE4提供了UPROPERTY来标记属性的反射数据,用UFUNCTION标记函数的反射数据,用UCLASS标记类的反射数据。 下面拿例子 ATestActor来说明UE4 C++反射的实现 (1)UClass存储了继承UObject的类反射信息, 用UCLASS宏来标记 ? (2)UScriptStruct存储了FXXX的结构体反射信息, USTRUCT宏来标记 ? ? (3)UFunction存储了函数的反射信息, 用UFUNCTION宏来标记 ? (4)FProperty存储属性的反射信息, 用UPROPERTY宏来标记 ? UClass类的具体机制UClass的经典引用判断一个对象是不是一个类? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 从上图UClass直接代表一个类的类数据, 同个类的类对象的UClass都是同一个, 存储了反射属性数据和反射函数数据等等。这里有个疑惑:UClass是怎么构造出来的?ATestActor::StaticClass() 哪里来的?答案很简单,用GENERATED_BODY 将 “TestActor.generated.h”?和 “TestActor.gen.cpp”?解析的代码包含进行UClass的构造。当然具体过程很长,下面我慢慢分析 GENERATED_BODY 首先看看ObjectMacros.h, 里面很多宏用于实现UE4 C++的反射机制 ? 再看 “TestActor.generated.h” 和TestActor.h ? ? 可以知道 CURRENT_FILE_ID?为 TestReflect_Source_TestReflect_TestActor_h。而__LINE__就是GENERATED_BODY所在行号12。? ?所以GENERATED_BODY 相当于TestReflect_Source_TestReflect_TestActor_h_12_GENERATED_BODY。? ? 这个宏其实在 TestActor.generated.h 也定义了,看看相关代码?????? ? TestReflect_Source_TestReflect_TestActor_h_12_PRIVATE_PROPERTY_OFFSET,TestReflect_Source_TestReflect_TestActor_h_12_SPARSE_DATA,TestReflect_Source_TestReflect_TestActor_h_12_RPC_WRAPPERS?等都是在TestActor.generated.h?声明的宏,展开来就是一堆代码, 拿TestReflect_Source_TestReflect_TestActor_h_12_RPC_WRAPPER。举个例子 ? 也就是说GENERATED_BODY的本质就是替换宏,把 宏 TestReflect_Source_TestReflect_TestActor_h_12_GENERATED_BODY展开的C++代码放到TestActor类里, 具体展开就是 ? 代码细节很多,很多具体宏没展开,比如 DECLARE_CLASS,?DECLARE_SERALIZER. 这里重点讲解的宏是DECLARE_CLASS,UClass*的生成和StaticClass()函数就和DECLARE_CLASS息息相关 ? 可以看到StaticClass()函数是怎么来的 ? ?这里GetPrivateStaticClass()怎么来的?在TestActor.gen.cpp的 ? ? ? ? ? ?水落石出, UClass*和StaticClass静态函数是怎么来的已经一目了然! 这个还有个很小的点, 多年前面试官喜欢问的,一个UObject怎么访问其母类的方法属性, 写过UE4 C++代码都知道是Super::XXX, 从上面DECLARE_CLASS?展开看到就是一个宏替换而已 ? ? 类(UClass)元数据的注册我们看到了UClass*的创建, 但是UClass的元数据呢?首先元数据本质就是Key-Vakue形式的值,可以和TMap<FString, String> 等同。类元数据在xxx.gen.cpp硬编码进行的,看TestActor.gen.cpp代码。 ? ? ? 上面的Z_Construct_UClass_ATestActor_Statics 是个结构体,包含了各种反射数据(类,属性,函数),可以看到都是静态变量, 这样不用实例化结构体就能保存反射数据了。这里有个小细节,WITH_METADATA说明元数据都是只在编辑器下存在。可以看到反射数据就是硬编码指认到静态变量,有个问题怎么把这个数据放到UClass*里,并且是什么时候放入到的?继续往下看,在TestActor.gen.cpp代码底部 ? 把各种静态反射数据(类元数据,属性元数据,函数元数据) 填充到FClassParams?ClassParams里? ? ?然后用FClassParams的数据对ATestActor的UClass进行赋值, 比如类元数据,属性数据,函数数据等。 ? ? ? ? 有个问题是Z_Construct_UClass_ATestActor函数什么时候执行? 答案在TestActor.gen.cpp代码的最底部 ? 这个本质上是个静态回调函数, 看看下面代码
? 每当一个模块(dll)加载进来,对应的UObject的UClass注册流程就开始进行了? 属性反射属性反射数据主要包括属性的名字, 属性的指针偏移(UE4的属性访问是通过变量相对于类的偏移量), 属性元数据等。其实属性反射实现的过程和类的元数据很类似, 都在TestActor.gen.cpp文件里硬编码的. ? ?
?最终一样是放到ClassParams里,最终被注册到UClass*数据了,从上图可以看出有多少个反射属性就对应多少对硬编码的属性数据(属性元数据以及属性基本信息). 函数反射函数反射和属性反射一样简单,函数反射主要包括函数的名字,函数的访问,函数的元数据等。 这里函数的各种数据和属性一样的硬编码的,在上面宏展开的代码可以看到 DECLARE_FUNCTION(execTestFunc) 进一步看 ? 在TestActor.gen.cpp可以看到 ? ??????? ? ? 对应的宏不细说,就是执行了TestFunc, 绕了一圈 这里函数指针的注册到UClass在StaticRegisterNativesATestActor中执行的 ? 至于StaticRegisterNativesATestActor是在上面IMPLAMENT_CLASS展开构造函数中进行的。 ? 至于函数的元数据, 和属性元数据构造过程差不多。 ? ? ?函数的元数据最终也到了ClassParams? UE4 C++ 反射 VS RTTR C++ 反射我之前一篇博客?RTTR反射原理剖解?分析了另一个C++反射框架 RTTR, 总体上是利用模板元编程进行类型萃取,而整个框架是非侵入式,而UE4的C++反射实现大体是预生成代码加上宏代码替换,硬编码来实现C++反射,从实现上我个人觉得RTTR更灵活(非侵入式),也更优雅(模板推导),当然RTTR明显使用比UE4更多的模板代码,可能会造成代码更慢编译速度问题。 资料参考【1】《InsideUE4》UObject(十三)类型系统-反射实战 【2】??RTTR(Run Time Type Reflection) C++反射原理实现剖解??????? |
|
C++知识库 最新文章 |
【C++】友元、嵌套类、异常、RTTI、类型转换 |
通讯录的思路与实现(C语言) |
C++PrimerPlus 第七章 函数-C++的编程模块( |
Problem C: 算法9-9~9-12:平衡二叉树的基本 |
MSVC C++ UTF-8编程 |
C++进阶 多态原理 |
简单string类c++实现 |
我的年度总结 |
【C语言】以深厚地基筑伟岸高楼-基础篇(六 |
c语言常见错误合集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年3日历 | -2025/3/4 8:58:29- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |