IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: 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

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TestActor.generated.h"

UCLASS(Blueprintable, meta = (DisplayName = "TestActor_Name", classGroup = "ssss"))
class TESTREFLECT_API ATestActor : public AActor
{
	GENERATED_BODY()
	

public:	
	// Sets default values for this actor's properties
	ATestActor();

	UPROPERTY(EditAnywhere, Category = "MyAAA")
		float a;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

	UPROPERTY()
		int32 b;

	UFUNCTION(BlueprintCallable, Category = "Snowing|BlueprintFunc")
		void TestFunc();

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

};

下面提供了UE4 C++反射的几种常用用法

	//获取对应基类的对象
	TArray<UObject*> result;
	GetObjectsOfClass(UClass::StaticClass(), result);

	TArray<UObject*> result1;
	GetObjectsOfClass(UEnum::StaticClass(), result1);

	TArray<UObject*> result2;
	GetObjectsOfClass(UScriptStruct::StaticClass(), result2);

	//遍历一个类的属性以及属性的元数据
	UClass* TestActorClass = ATestActor::StaticClass();
	for (TFieldIterator<FProperty> P(TestActorClass); P; ++P)
	{
		FProperty* prt = *P;
		const TMap<FName, FString>* metaData = prt->GetMetaDataMap();
	}

	//遍历一个类的函数
	for (TFieldIterator<UFunction> F(TestActorClass); F; ++F)
	{
		UFunction* fun = *F;
	}

	//UClass 元数据
	FString test = TestActorClass->GetMetaData("classGroup");

	//Object的额外元数据 仅在编辑器的时候有用
	UMetaData* metaData = this->GetOutermost()->GetMetaData();
	metaData->SetValue(this, FName("Test"), TEXT("TestActor"));

	TMap<FName, FString>* keyValues = metaData->GetMapForObject(this);
	if (keyValues != nullptr&&keyValues->Num() > 0)
	{
		for (const auto& i : *keyValues)
		{
			FName key = i.Key;
			FString value = i.Value;
		}
	}

UE4 C++反射的实现原理剖解

CPP反射总体流程

?

?说明一下UE4 CPP反射宏(UClass, UPROPERTY, UFUNCTION)是空宏,最后根本没有参与到C++编译当中,具体见ObjectMacros.h

?

UBT

UBT全称UnrealBuildTool, 用在建立项目配置,如模块依赖,包含文件目录,设置项目优化配置项等,作用类似于premake, cmake这些东西,就我看到的代码而言,UE4 C++ 反射和UBT几乎没什么关系。UnrealBuildTool.exe文件Engine\Source\Programs\UnrealBuildTool\Development

UHT

UHT 全称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_OFFSETTestReflect_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文件里硬编码的.

?

?

const UE4CodeGen_Private::FFloatPropertyParams Z_Construct_UClass_ATestActor_Statics::NewProp_a = { "a", nullptr, (EPropertyFlags)0x0010000000000001, UE4CodeGen_Private::EPropertyGenFlags::Float, RF_Public|RF_Transient|RF_MarkAsNative, 1, STRUCT_OFFSET(ATestActor, a), METADATA_PARAMS(Z_Construct_UClass_ATestActor_Statics::NewProp_a_MetaData, UE_ARRAY_COUNT(Z_Construct_UClass_ATestActor_Statics::NewProp_a_MetaData)) };

?最终一样是放到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语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-06 09:26:01  更:2021-08-06 09:27:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/9 5:55:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码