UE4反射机制主要分为以下几个阶段
- 生成阶段:借助UHT生成对应包含了反射声明的反射代码
- 收集阶段:借助static自动注册方式,在模块加载的时候,把UCLASS注册,放在array中管理
- 注册阶段:在模块初始化的时候,将Array中的所有UClass相关的Function和Property注册;
- 链接阶段:就是反射功能。
第一阶段
在介绍第一阶段前,先介绍两个工具,ubt和uht。UBT是UE4自带的C++工具,用来处理文件依赖,包含。(待理解)
UHT则是UE4自带的C++解析工具,借助UBT提供的文件依赖关系,对包含了反射作用的文件进行集中编译处理,生成对应的反射代码,放在.generate.h 和 .generate.cpp。
一个C++文件如果包含了反射代码,那么他一定要include generate.h,不然UHT无法得知有哪些文件需要被反射编译。
20200919更新
过了这么长时间终于又有时间(勇气)再次开始看UE4的反射机制。之前看的很快,但很多模块都只是一知半解,甚至一头雾水,找了很多博主的博客和文章,现在再来看,好像稍微懂了一些,感觉还是不能急于求成,尽量每天看一点,消化理解,积少成多。
之前说到的generate.h,其实就是UE对我们自己写的h文件产生的反射代码文件。
在说UE4的反射代码生成之前,先看说一些反射是什么,其实说白了就是在蓝图里能够动态创建C++的对象类型(比如右键点击生成一个我们定义的函数,成员,类等等)如果要做到这个,我们就得在运行的时候,动态获得我们写的C++的代码结构的类型,比如写了一个int cint;我们在蓝图里就要获取这个cint的类型,写一个函数,我们就可以动态调用这个函数,生成入口和出口。
C++有RTTI可以在运行的时候获得变量的类型信息。但感觉功能还是简单了一些,不能满足蓝图的功能,还有宏,利用宏生成类型信息实现反射,我在网上找了一个易于理解的方法转在博客里,传送门在这里【C++】利用宏实现反射
这样写起来比较复杂,但是UE当前采用的就是这种方法(不过要复杂的多),通过对代码进行宏标记,生成对应的.generated.h/cpp文件。
通过宏标记能够为我们的代码生成反射代码,并且保存在generated.h/cpp中,我们需要反射的类型有哪些呢?如下图
从下向上分析:
- UFunction 函数反射
- UClass C++类
- UScriptStruct C++中的结构体
- Ustruct 以上三种都属于这个
- UProperty 成员变量类型
- UEnum 枚举
- UField 包含成员变量,广义结构体和枚举
- UInterface 是一个特殊的接口类
- UMetaData 元数据 在宏标记的时候有遇到元数据说明符,来定义一些辅助信息提示,可理解成TMap<FName,FString>。
- 所有的这些类型都属于UObject 好处是这些类都不用自己写GC和序列化了,因为UObject已经帮他们实现了。
这些类型就是在我们需要从蓝图中获取信息的时候所依赖的索引类型,比如C++中我们写了:
UPROPERTY()
int cint;
那么这个cint就是属于UProperty类型的派生类。
知道了UE的反射类型结构之后,我们需要看一下UE如何为我们的代码生成反射需要的代码。
看了大钊的InsideUE,讲的非常详细,但是很多东西还是有点难以理解。在这里整理一下。
UHT为我们的代码生成了4个文件,我们需要关注其中的两个,如我们写了一个MyClass.h。我们需要关注
- MyClass.generated.h MyClass生成的头文件
- ProjectName.generated.cpp 项目的编译文件
首先MyClass.h文件如下
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "UObject/NoExportTypes.h"
#include "MyClass.generated.h"
UCLASS()
class HELLO_API UMyClass : public UObject
{
GENERATED_BODY()
};
很简单,只是在类前面加一个宏定义,然后在类里面添加一个宏,GENERATED_BODY()
关键来了!GENERATED_BODY()
#define BODY_MACRO_COMBINE_INNER(A,B,C,D) A##B##C##D
#define BODY_MACRO_COMBINE(A,B,C,D) BODY_MACRO_COMBINE_INNER(A,B,C,D)
#define GENERATED_BODY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY)
20200921更新
可以看到GENERATED_BODY()其实就是生成了一个字符串名字Hello_Source_Hello_MyClass_h_11_GENERATED_BODY,这个字符串宏会在.generated.h中再定义展开。
如果类需要构造函数自定义实现,会让GENERATED_BODY()指向生成的另一个字符串Hello_Source_Hello_MyClass_h_11_GENERATED_BODY_LEGACY。
接下来就是这两个宏的展开:
#define Hello_Source_Hello_MyClass_h_11_GENERATED_BODY_LEGACY \ //两个重要的定义
PRAGMA_DISABLE_DEPRECATION_WARNINGS \
public: \
Hello_Source_Hello_MyClass_h_11_PRIVATE_PROPERTY_OFFSET \ 11
Hello_Source_Hello_MyClass_h_11_RPC_WRAPPERS \ 12
Hello_Source_Hello_MyClass_h_11_INCLASS \ 13
Hello_Source_Hello_MyClass_h_11_STANDARD_CONSTRUCTORS \ 14
public: \
PRAGMA_ENABLE_DEPRECATION_WARNINGS
#define Hello_Source_Hello_MyClass_h_11_GENERATED_BODY \ //两个重要的定义
PRAGMA_DISABLE_DEPRECATION_WARNINGS \
public: \
Hello_Source_Hello_MyClass_h_11_PRIVATE_PROPERTY_OFFSET \ 21
Hello_Source_Hello_MyClass_h_11_RPC_WRAPPERS_NO_PURE_DECLS \ 22
Hello_Source_Hello_MyClass_h_11_INCLASS_NO_PURE_DECLS \ 23
Hello_Source_Hello_MyClass_h_11_ENHANCED_CONSTRUCTORS \ 24
private: \
PRAGMA_ENABLE_DEPRECATION_WARNINGS
两个宏又分别定义了八个宏,按照编号来看。13和23的定义是一样的,14和24的定义略微有些区别,差了一个构造函数的默认实现。先看14的宏定义
#define Hello_Source_Hello_MyClass_h_11_ENHANCED_CONSTRUCTORS \
/** Standard constructor, called after all reflected properties have been initialized */ \
NO_API UMyClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { }; \ //默认的构造函数实现
private: \ //禁止掉C++11的移动和拷贝构造
/** Private move- and copy-constructors, should never be used */ \
NO_API UMyClass(UMyClass&&); \
NO_API UMyClass(const UMyClass&); \
public: \
DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, UMyClass); \ //因为WITH_HOT_RELOAD_CTORS关闭,展开是空宏
DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyClass); \ //同理,空宏
DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyClass)
这里又定义了一些宏,最后一个是 DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyClass)
这个宏的定义如下:
#define DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(TClass) \
static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass(X); }
可以看到生成了一个静态函数,这个函数的实现其实是调用了类的构造函数。 因为构造函数不能用函数指针,所以做了一层包装。而UClass的构造函数签名都一样,所以可以用静态函数来统一包装调用,然后用一个函数指针调用这个静态函数。
函数签名就是一个函数的名称和调用参数列表。
这里的EInternal*是一个枚举值,只用来构造内部使用。FObjectInitiallizer是一个用来构造使用的类,在C++构造函数调用之后用来真正的构造一个UObject。所以这个内部实现的语句意思就是
new((Enum*)Uobject*)TClass(X)没看太明白
这里应该是调用了placement new 函数 在返回的内存上去构造我们的UCLASS
然后是宏23(编号见前),这个宏又定义了一堆宏,其中最重要的是DECLARE_CLASS,是对这个类的一个声明,展开见下
#define Hello_Source_Hello_MyClass_h_11_INCLASS \
private: \
static void StaticRegisterNativesUMyClass(); \ //定义在cpp中,目前都是空实现
friend HELLO_API class UClass* Z_Construct_UClass_UMyClass(); \ //一个构造该类UClass对象的辅助函数
public: \
DECLARE_CLASS(UMyClass, UObject, COMPILED_IN_FLAGS(0), 0, TEXT("/Script/Hello"), NO_API) \ //声明该类的一些通用基本函数
DECLARE_SERIALIZER(UMyClass) \ //声明序列化函数
/** Indicates whether the class is compiled into the engine */ \
enum {IsIntrinsic=COMPILED_IN_INTRINSIC}; //这个标记指定了该类是C++Native类,不能动态再改变,跟蓝图里构造的动态类进行区分。
#define DECLARE_CLASS( TClass, TSuperClass, TStaticFlags, TStaticCastFlags, TPackage, TRequiredAPI ) \
private: \
TClass& operator=(TClass&&); \
TClass& operator=(const TClass&); \
TRequiredAPI static UClass* GetPrivateStaticClass(const TCHAR* Package); \
public: \
/** Bitwise union of #EClassFlags pertaining to this class.*/ \
enum {StaticClassFlags=TStaticFlags}; \
/** Typedef for the base class ({{ typedef-type }}) */ \
typedef TSuperClass Super;\
/** Typedef for {{ typedef-type }}. */ \
typedef TClass ThisClass;\
/** Returns a UClass object representing this class at runtime */ \
inline static UClass* StaticClass() \
{ \
return GetPrivateStaticClass(TPackage); \
} \
/** Returns the StaticClassFlags for this class */ \
inline static EClassCastFlags StaticClassCastFlags() \
{ \
return TStaticCastFlags; \
} \
DEPRECATED(4.7, "operator new has been deprecated for UObjects - please use NewObject or NewNamedObject instead") \
inline void* operator new( const size_t InSize, UObject* InOuter=(UObject*)GetTransientPackage(), FName InName=NAME_None, EObjectFlags InSetFlags=RF_NoFlags ) \
{ \
return StaticAllocateObject( StaticClass(), InOuter, InName, InSetFlags ); \
} \
/** For internal use only; use StaticConstructObject() to create new objects. */ \
inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags) \
{ \
return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags); \
} \
/** For internal use only; use StaticConstructObject() to create new objects. */ \
inline void* operator new( const size_t InSize, EInternal* InMem ) \
{ \
return (void*)InMem; \
}
DECLARE_CLASS的一些函数定义其实就是我们普通在定义类的时候实现的一些函数。其中有一个值得注意的是StaticClass,这个函数定义在ProjectName.generated.cpp中。
#include "Hello.h" //包含该项目的头文件,继而包含Engine.h
#include "GeneratedCppIncludes.h" //包含UObject模块里一些必要头文件
#include "Hello.generated.dep.h" //引用依赖文件,继而include了MyClass.h
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void EmptyLinkFunctionForGeneratedCode1Hello() {} //先忽略
void UMyClass::StaticRegisterNativesUMyClass() //说是静态注册,但现在都是为空,先忽略
{
}
IMPLEMENT_CLASS(UMyClass, 899540749); //重要!!!
#if USE_COMPILED_IN_NATIVES //该宏编译的时候会打开
// Cross Module References
COREUOBJECT_API class UClass* Z_Construct_UClass_UObject(); //引用CoreUObject里的函数,主要是为了得到UObject本身对应的UClass
HELLO_API class UClass* Z_Construct_UClass_UMyClass_NoRegister(); //构造UMyClass对应的UClass对象,区别是没有后续的注册过程
HELLO_API class UClass* Z_Construct_UClass_UMyClass(); //构造UMyClass对应的UClass对象
HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello(); //构造Hello本身的UPackage对象
UClass* Z_Construct_UClass_UMyClass_NoRegister()
{
return UMyClass::StaticClass(); //直接通过访问来获取UClass对象
}
UClass* Z_Construct_UClass_UMyClass() //构造并注册
{
static UClass* OuterClass = NULL; //static lazy模式
if (!OuterClass)
{
Z_Construct_UClass_UObject(); //确保UObject本身的UClass已经注册生成
Z_Construct_UPackage__Script_Hello(); //确保当前Hello项目的UPackage已经创建,因为后续在生成UMyClass的UClass*对象时需要保存在这个UPacage中
OuterClass = UMyClass::StaticClass(); //访问获得UClass*
if (!(OuterClass->ClassFlags & CLASS_Constructed)) //防止重复注册
{
UObjectForceRegistration(OuterClass); //提取信息注册自身
OuterClass->ClassFlags |= 0x20100080; //增加CLASS_Constructed|CLASS_RequiredAPI标记
OuterClass->StaticLink(); //“静态”链接,后续解释
#if WITH_METADATA //编辑器模式下开始
UMetaData* MetaData = OuterClass->GetOutermost()->GetMetaData(); //获取关联到的UPackage其身上的元数据映射,并增加元数据信息
MetaData->SetValue(OuterClass, TEXT("IncludePath"), TEXT("MyClass.h"));
MetaData->SetValue(OuterClass, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));
#endif
}
}
check(OuterClass->GetClass());
return OuterClass;
}
static FCompiledInDefer Z_CompiledInDefer_UClass_UMyClass(Z_Construct_UClass_UMyClass, &UMyClass::StaticClass, TEXT("UMyClass"), false, nullptr, nullptr, nullptr); //延迟注册,注入信息,在启动的时候调用
DEFINE_VTABLE_PTR_HELPER_CTOR(UMyClass); //HotReload相关,先忽略
UPackage* Z_Construct_UPackage__Script_Hello() //构造Hello的UPackage
{
static UPackage* ReturnPackage = NULL;
if (!ReturnPackage)
{
ReturnPackage = CastChecked<UPackage>(StaticFindObjectFast(UPackage::StaticClass(), NULL, FName(TEXT("/Script/Hello")), false, false));//注意的是这里只是做一个查找,真正的CreatePackage是在UObjectBase::DeferredRegister里调用的,后续在流程里会讨论到
ReturnPackage->SetPackageFlags(PKG_CompiledIn | 0x00000000);//设定标记和Guid
FGuid Guid;
Guid.A = 0x79A097CD;
Guid.B = 0xB58D8B48;
Guid.C = 0x00000000;
Guid.D = 0x00000000;
ReturnPackage->SetGuid(Guid);
}
return ReturnPackage;
}
#endif
PRAGMA_ENABLE_DEPRECATION_WARNINGS
这里要看的是IMPLEMENT_CLASS和.h中的DECLARE_CLASS对应,定义如下
#define IMPLEMENT_CLASS(TClass, TClassCrc) \
static TClassCompiledInDefer<TClass> AutoInitialize##TClass(TEXT(#TClass), sizeof(TClass), TClassCrc); \ //延迟注册
UClass* TClass::GetPrivateStaticClass(const TCHAR* Package) \ //.h里声明的实现,StaticClas()内部就是调用该函数
{ \
static UClass* PrivateStaticClass = NULL; \ //又一次static lazy
if (!PrivateStaticClass) \
{ \
/* this could be handled with templates, but we want it external to avoid code bloat */ \
GetPrivateStaticClassBody( \ //该函数就是真正创建UClass*,以后
Package, \ //Package名字
(TCHAR*)TEXT(#TClass) + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0), \//类名,+1去掉U、A、F前缀,+11去掉_Deprecated前缀
PrivateStaticClass, \ //输出引用
StaticRegisterNatives##TClass, \
sizeof(TClass), \
TClass::StaticClassFlags, \
TClass::StaticClassCastFlags(), \
TClass::StaticConfigName(), \
(UClass::ClassConstructorType)InternalConstructor<TClass>, \
(UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<TClass>, \
&TClass::AddReferencedObjects, \
&TClass::Super::StaticClass, \
&TClass::WithinClass::StaticClass \
); \
} \
return PrivateStaticClass; \
}
这个宏定义了静态的构造函数模板和对应模板类的一个GetPrivateStaticClass函数,把参数Package类的信息传递给GetPrivateStaticClassBody()函数,最终GetPrivateStaticClassBody会在一个全局的数组中(用宏来实现模板,太妙了)
那这个函数到底在哪里被实现调用呢?且听后文分析
最终之前写的Class.h,由于添加了一个GENERATED_BODY(),被展开成了一长串宏,宏之后又展开宏...
#pragma once
#include "UObject/NoExportTypes.h"
class HELLO_API UMyClass : public UObject
{
private:
static void StaticRegisterNativesUMyClass();
friend HELLO_API class UClass* Z_Construct_UClass_UMyClass();
private:
UMyClass& operator=(UMyClass&&);
UMyClass& operator=(const UMyClass&);
NO_API static UClass* GetPrivateStaticClass(const TCHAR* Package);
public:
/** Bitwise union of #EClassFlags pertaining to this class.*/
enum {StaticClassFlags = CLASS_Intrinsic};
/** Typedef for the base class ({{ typedef-type }}) */
typedef UObject Super;
/** Typedef for {{ typedef-type }}. */
typedef UMyClass ThisClass;
/** Returns a UClass object representing this class at runtime */
inline static UClass* StaticClass()
{
return GetPrivateStaticClass(TEXT("/Script/Hello"));
}
/** Returns the StaticClassFlags for this class */
inline static EClassCastFlags StaticClassCastFlags()
{
return 0;
}
DEPRECATED(4.7, "operator new has been deprecated for UObjects - please use NewObject or NewNamedObject instead")
inline void* operator new(const size_t InSize, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags)
{
return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags);
}
/** For internal use only; use StaticConstructObject() to create new objects. */
inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags)
{
return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags);
}
/** For internal use only; use StaticConstructObject() to create new objects. */
inline void* operator new(const size_t InSize, EInternal* InMem)
{
return (void*)InMem;
}
friend FArchive &operator<<(FArchive& Ar, UMyClass*& Res)
{
return Ar << (UObject*&)Res;
}
/** Indicates whether the class is compiled into the engine */
enum { IsIntrinsic = COMPILED_IN_INTRINSIC };
/** Standard constructor, called after all reflected properties have been initialized */
NO_API UMyClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { };
private:
/** Private move- and copy-constructors, should never be used */
NO_API UMyClass(UMyClass&&);
NO_API UMyClass(const UMyClass&);
public:
static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())UMyClass(X); }
};
其中只是声明了一些New,和重载了一些操作符,以及把移动和拷贝构造函数私有化了,还声明了一个StaticClass()函数,这个比较关键。GENERATED_BODY()的展开就到这里啦。
总结
UE4会为Myclass.h生成.generated.h和projectname.generated.cpp。在h文件中主要是包含GENERATED_BODY()的宏定义展开,展开包括八个宏,这里只涉及后四个,每个宏又可以分别展开,画一个展开层次图如下:
然后generated.cpp中包含了一堆Z开头的函数辅助函数,这里先不去管他,还有一个宏定义IMPLEMENT_CLASS,这个宏定义中定义了StaticClass的函数实现,就是把类的信息放入GetPrivateStaticClassBody()函数中。
到这里位置,只是把代码中的宏进行了展开,至于类如何调用构造,如何形成反射还有很长的路,希望接下来能有时间把这部分内容一点点全都看完写下来。
这里其实已经有了一点线索,就是在projectname.generated.cpp的代码展开中定义了两个static变量,以我们创建的class举例,有这俩:
static TClassCompiledInDefer<UMyClass> AutoInitializeUMyClass(TEXT("UMyClass"), sizeof(UMyClass), 899540749);
static FCompiledInDefer Z_CompiledInDefer_UClass_UMyClass(Z_Construct_UClass_UMyClass, &UMyClass::StaticClass, TEXT("UMyClass"), false, nullptr, nullptr, nullptr);
这两个静态变量会在main执行之前就调用变量的构造函数,同时会先执行传进去的参数~,两个静态变量对于信息记录的作用也不一样,这里留个坑,下次讲解~
下面是Enum的代码生成内容
正好最近没什么活,趁着晚上再把Enum的展开看一下,写的.h文件如下
#pragma once
#include "UObject/NoExportTypes.h"
#include "MyEnum.generated.h"
UENUM(BlueprintType)
enum class EMyEnum : uint8
{
MY_Dance UMETA(DisplayName = "Dance"),
MY_Rain UMETA(DisplayName = "Rain"),
MY_Song UMETA(DisplayName = "Song")
};
生成的MyEnum.generated.h为
PRAGMA_DISABLE_DEPRECATION_WARNINGS
#ifdef HELLO_MyEnum_generated_h
#error "MyEnum.generated.h already included, missing '#pragma once' in MyEnum.h"
#endif
#define HELLO_MyEnum_generated_h
#undef CURRENT_FILE_ID
#define CURRENT_FILE_ID Hello_Source_Hello_MyEnum_h
#define FOREACH_ENUM_EMYENUM(op) \ //定义一个遍历枚举值的宏,只是为了方便使用
op(EMyEnum::MY_Dance) \
op(EMyEnum::MY_Rain) \
op(EMyEnum::MY_Song)
PRAGMA_ENABLE_DEPRECATION_WARNINGS
这里只有一个宏定义,是关于枚举值的一个遍历
在generated.cpp中像之前的StaticClass接口一样也定义了一个StaticEnum接口,内部调用了GetStaticEnum接口,其内部又调用了Z_Construct_UEnum_Hello_EMyEnum,嵌套了三层提供了一个返回静态UEnum指针的接口。
和上面的class 一样,这里也留下了一个静态变量
static FCompiledInDeferEnum Z_CompiledInDeferEnum_UEnum_EMyEnum(EMyEnum_StaticEnum, TEXT("/Script/Hello"), TEXT("EMyEnum"), false, nullptr, nullptr); //延迟注册
在构造的时候,会先执行自己的参数StaticEnum,然后一层一层的调用下去。
USTRUCT的代码生成
定义的USTRUCT结构如下
#pragma once
#include "UObject/NoExportTypes.h"
#include "MyStruct.generated.h"
USTRUCT(BlueprintType)
struct HELLO_API FMyStruct
{
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadWrite)
float Score;
};
生成的MyStruct.generated.h如下:
PRAGMA_DISABLE_DEPRECATION_WARNINGS
#ifdef HELLO_MyStruct_generated_h
#error "MyStruct.generated.h already included, missing '#pragma once' in MyStruct.h"
#endif
#define HELLO_MyStruct_generated_h
#define Hello_Source_Hello_MyStruct_h_8_GENERATED_BODY \
friend HELLO_API class UScriptStruct* Z_Construct_UScriptStruct_FMyStruct(); \ //给予全局方法友元权限
static class UScriptStruct* StaticStruct(); //静态函数返回UScriptStruct*
#undef CURRENT_FILE_ID
#define CURRENT_FILE_ID Hello_Source_Hello_MyStruct_h
PRAGMA_ENABLE_DEPRECATION_WARNINGS
最终在projectname.genrated.cpp里也会生成两个static变量,用在Main之前注册
分别是
static FCompiledInDeferStruct Z_CompiledInDeferStruct_UScriptStruct_FMyStruct(FMyStruct::StaticStruct, TEXT("/Script/Hello"), TEXT("MyStruct"), false, nullptr, nullptr); //延迟注册
ScriptStruct_Hello_StaticRegisterNativesFMyStruct
生成的projectname.generated.cpp如下
#include "Hello.h"
#include "GeneratedCppIncludes.h"
#include "Hello.generated.dep.h"
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void EmptyLinkFunctionForGeneratedCode1Hello() {}
class UScriptStruct* FMyStruct::StaticStruct()//实现了静态获取UScriptStruct*
{
extern HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello();
static class UScriptStruct* Singleton = NULL;
if (!Singleton)
{
extern HELLO_API class UScriptStruct* Z_Construct_UScriptStruct_FMyStruct();
extern HELLO_API uint32 Get_Z_Construct_UScriptStruct_FMyStruct_CRC();
Singleton = GetStaticStruct(Z_Construct_UScriptStruct_FMyStruct, Z_Construct_UPackage__Script_Hello(), TEXT("MyStruct"), sizeof(FMyStruct), Get_Z_Construct_UScriptStruct_FMyStruct_CRC());
}
return Singleton;
}
static FCompiledInDeferStruct Z_CompiledInDeferStruct_UScriptStruct_FMyStruct(FMyStruct::StaticStruct, TEXT("/Script/Hello"), TEXT("MyStruct"), false, nullptr, nullptr); //延迟注册
static struct FScriptStruct_Hello_StaticRegisterNativesFMyStruct
{
FScriptStruct_Hello_StaticRegisterNativesFMyStruct()
{
UScriptStruct::DeferCppStructOps(FName(TEXT("MyStruct")),new UScriptStruct::TCppStructOps<FMyStruct>);
}
} ScriptStruct_Hello_StaticRegisterNativesFMyStruct; //static注册
#if USE_COMPILED_IN_NATIVES
HELLO_API class UScriptStruct* Z_Construct_UScriptStruct_FMyStruct();
HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello();
UScriptStruct* Z_Construct_UScriptStruct_FMyStruct() //构造关联的UScriptStruct*
{
UPackage* Outer = Z_Construct_UPackage__Script_Hello();
extern uint32 Get_Z_Construct_UScriptStruct_FMyStruct_CRC();
static UScriptStruct* ReturnStruct = FindExistingStructIfHotReloadOrDynamic(Outer, TEXT("MyStruct"), sizeof(FMyStruct), Get_Z_Construct_UScriptStruct_FMyStruct_CRC(), false);
if (!ReturnStruct)
{
ReturnStruct = new(EC_InternalUseOnlyConstructor, Outer, TEXT("MyStruct"), RF_Public|RF_Transient|RF_MarkAsNative) UScriptStruct(FObjectInitializer(), NULL, new UScriptStruct::TCppStructOps<FMyStruct>, EStructFlags(0x00000201));//直接创建UScriptStruct对象
UProperty* NewProp_Score = new(EC_InternalUseOnlyConstructor, ReturnStruct, TEXT("Score"), RF_Public|RF_Transient|RF_MarkAsNative) UFloatProperty(CPP_PROPERTY_BASE(Score, FMyStruct), 0x0010000000000004);//直接关联相应的Property信息
ReturnStruct->StaticLink(); //链接
#if WITH_METADATA //元数据
UMetaData* MetaData = ReturnStruct->GetOutermost()->GetMetaData();
MetaData->SetValue(ReturnStruct, TEXT("BlueprintType"), TEXT("true"));
MetaData->SetValue(ReturnStruct, TEXT("ModuleRelativePath"), TEXT("MyStruct.h"));
MetaData->SetValue(NewProp_Score, TEXT("Category"), TEXT("MyStruct"));
MetaData->SetValue(NewProp_Score, TEXT("ModuleRelativePath"), TEXT("MyStruct.h"));
#endif
}
return ReturnStruct;
}
uint32 Get_Z_Construct_UScriptStruct_FMyStruct_CRC() { return 2914362188U; }
UPackage* Z_Construct_UPackage__Script_Hello()
{
...略
}
#endif
PRAGMA_ENABLE_DEPRECATION_WARNINGS
当然这里也包含了一堆Z开头的构造函数,会在之后的流程调用。当然这里为了和UCLASS还有UENUM保持一致,肯定会有一个staticstruct的函数。内部经过一系列封装,会调用Z_Construct_UScriptStruct_FMyStruct函数来完成元数据的绑定。
第二个静态变量会主动调用UScriptStruct::DeferCppStructOps想程序注册结构的CPP信息,类似于TClassCompiledInDefer<TClass>
最后一个是UINTERFACE的代码生成,和UCLASS基本一致,生成的两个静态变量也是高度一致
一个在IMPLEMENT_CLASS中定义的TClassCompiledInDefer<TClass>
一个是static FCompiledInDefer Z_CompiledInDefer_UClass_UMyInterface
和UCLASS不同的地方是会在Z构造函数中添加一些元数据和函数链接。
在UCLASS里添加函数和属性之后的代码生成
#pragma once
#include "UObject/NoExportTypes.h"
#include "MyClass.generated.h"
UCLASS(BlueprintType)
class HELLO_API UMyClass : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite)
float Score;
public:
UFUNCTION(BlueprintCallable, Category = "Hello")
void CallableFunc(); //C++实现,蓝图调用
UFUNCTION(BlueprintNativeEvent, Category = "Hello")
void NativeFunc(); //C++实现默认版本,蓝图可重载实现
UFUNCTION(BlueprintImplementableEvent, Category = "Hello")
void ImplementableFunc(); //C++不实现,蓝图实现
};
生成的generated.h如下
#define Hello_Source_Hello_MyClass_h_8_RPC_WRAPPERS \
virtual void NativeFunc_Implementation(); \ //默认实现的函数声明,我们可以自己实现
\
DECLARE_FUNCTION(execNativeFunc) \ //声明供蓝图调用的函数
{ \
P_FINISH; \
P_NATIVE_BEGIN; \
this->NativeFunc_Implementation(); \
P_NATIVE_END; \
} \
\
DECLARE_FUNCTION(execCallableFunc) \ //声明供蓝图调用的函数
{ \
P_FINISH; \
P_NATIVE_BEGIN; \
this->CallableFunc(); \
P_NATIVE_END; \
}
#define Hello_Source_Hello_MyClass_h_8_RPC_WRAPPERS_NO_PURE_DECLS \ //和上面重复,略
//声明函数名称
extern HELLO_API FName HELLO_ImplementableFunc;
extern HELLO_API FName HELLO_NativeFunc;
这里写了两个函数的实现,execNativeFunc和execCallableFunc,因为ImplementableFunc是蓝图实现的,所以我们在C++里不做实现。这个exec的函数,就是蓝图虚拟机调用的函数,最终会调用到我们C++里的实现。
而在projectname.genrated.cpp里
//函数名字定义
FName HELLO_ImplementableFunc = FName(TEXT("ImplementableFunc"));
FName HELLO_NativeFunc = FName(TEXT("NativeFunc"));
void UMyClass::ImplementableFunc() //C++端的实现
{
ProcessEvent(FindFunctionChecked(HELLO_ImplementableFunc),NULL);
}
void UMyClass::NativeFunc() //C++端的实现
{
ProcessEvent(FindFunctionChecked(HELLO_NativeFunc),NULL);
}
void UMyClass::StaticRegisterNativesUMyClass() //注册函数名字和函数指针映射
{
FNativeFunctionRegistrar::RegisterFunction(UMyClass::StaticClass(), "CallableFunc",(Native)&UMyClass::execCallableFunc);
FNativeFunctionRegistrar::RegisterFunction(UMyClass::StaticClass(), "NativeFunc",(Native)&UMyClass::execNativeFunc);
}
//...略去中间相同部分
//构造3个函数的UFunction*对象,结构一样,只是EFunctionFlags不一样
UFunction* Z_Construct_UFunction_UMyClass_CallableFunc()
{
UObject* Outer=Z_Construct_UClass_UMyClass();
static UFunction* ReturnFunction = NULL;
if (!ReturnFunction)
{
ReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT("CallableFunc"), RF_Public|RF_Transient|RF_MarkAsNative) UFunction(FObjectInitializer(), NULL, 0x04020401, 65535); //FUNC_BlueprintCallable|FUNC_Public|FUNC_Native|FUNC_Final
ReturnFunction->Bind();
ReturnFunction->StaticLink();
#if WITH_METADATA
UMetaData* MetaData = ReturnFunction->GetOutermost()->GetMetaData();
MetaData->SetValue(ReturnFunction, TEXT("Category"), TEXT("Hello"));
MetaData->SetValue(ReturnFunction, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));
#endif
}
return ReturnFunction;
}
UFunction* Z_Construct_UFunction_UMyClass_ImplementableFunc()
{
UObject* Outer=Z_Construct_UClass_UMyClass();
static UFunction* ReturnFunction = NULL;
if (!ReturnFunction)
{
ReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT("ImplementableFunc"), RF_Public|RF_Transient|RF_MarkAsNative) UFunction(FObjectInitializer(), NULL, 0x08020800, 65535); //FUNC_BlueprintEvent|FUNC_Public|FUNC_Event
ReturnFunction->Bind();
ReturnFunction->StaticLink();
#if WITH_METADATA
UMetaData* MetaData = ReturnFunction->GetOutermost()->GetMetaData();
MetaData->SetValue(ReturnFunction, TEXT("Category"), TEXT("Hello"));
MetaData->SetValue(ReturnFunction, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));
#endif
}
return ReturnFunction;
}
UFunction* Z_Construct_UFunction_UMyClass_NativeFunc()
{
UObject* Outer=Z_Construct_UClass_UMyClass();
static UFunction* ReturnFunction = NULL;
if (!ReturnFunction)
{
ReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT("NativeFunc"), RF_Public|RF_Transient|RF_MarkAsNative) UFunction(FObjectInitializer(), NULL, 0x08020C00, 65535);//FUNC_BlueprintEvent|FUNC_Public|FUNC_Event|FUNC_Native
ReturnFunction->Bind();
ReturnFunction->StaticLink();
#if WITH_METADATA
UMetaData* MetaData = ReturnFunction->GetOutermost()->GetMetaData();
MetaData->SetValue(ReturnFunction, TEXT("Category"), TEXT("Hello"));
MetaData->SetValue(ReturnFunction, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));
#endif
}
return ReturnFunction;
}
//...略去中间相同部分
UClass* Z_Construct_UClass_UMyClass()
{
static UClass* OuterClass = NULL;
if (!OuterClass)
{
Z_Construct_UClass_UObject();
Z_Construct_UPackage__Script_Hello();
OuterClass = UMyClass::StaticClass();
if (!(OuterClass->ClassFlags & CLASS_Constructed))
{
UObjectForceRegistration(OuterClass);
OuterClass->ClassFlags |= 0x20100080;
//添加子字段
OuterClass->LinkChild(Z_Construct_UFunction_UMyClass_CallableFunc());
OuterClass->LinkChild(Z_Construct_UFunction_UMyClass_ImplementableFunc());
OuterClass->LinkChild(Z_Construct_UFunction_UMyClass_NativeFunc());
PRAGMA_DISABLE_DEPRECATION_WARNINGS
UProperty* NewProp_Score = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("Score"), RF_Public|RF_Transient|RF_MarkAsNative) UFloatProperty(CPP_PROPERTY_BASE(Score, UMyClass), 0x0010000000000004);//添加属性
PRAGMA_ENABLE_DEPRECATION_WARNINGS
//添加函数名字映射
OuterClass->AddFunctionToFunctionMapWithOverriddenName(Z_Construct_UFunction_UMyClass_CallableFunc(), "CallableFunc"); // 774395847
OuterClass->AddFunctionToFunctionMapWithOverriddenName(Z_Construct_UFunction_UMyClass_ImplementableFunc(), "ImplementableFunc"); // 615168156
OuterClass->AddFunctionToFunctionMapWithOverriddenName(Z_Construct_UFunction_UMyClass_NativeFunc(), "NativeFunc"); // 3085959641
OuterClass->StaticLink();
#if WITH_METADATA //元数据
UMetaData* MetaData = OuterClass->GetOutermost()->GetMetaData();
MetaData->SetValue(OuterClass, TEXT("BlueprintType"), TEXT("true"));
MetaData->SetValue(OuterClass, TEXT("IncludePath"), TEXT("MyClass.h"));
MetaData->SetValue(OuterClass, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));
MetaData->SetValue(NewProp_Score, TEXT("Category"), TEXT("MyClass"));
MetaData->SetValue(NewProp_Score, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));
#endif
}
}
check(OuterClass->GetClass());
return OuterClass;
}
Cpp里写了两个函数的定义ImplementableFunc和NativeFunc,因为CallableFunc函数是C++实现的,所以CPP里没有生成实现,如果CallableFunc没有实现就编译不过了嘛,除此之外,Cpp里还多了三个UFUNCTION对象的构造函数,以及在UCLASS的构造函数里加上了UFUNCTION的函数链接以及元数据设置。
可以看出,对于CallableFunc这种C++实现,蓝图只是调用的方法,生成的代码只是生成了相应的UFunction*对象。而对于NativeFunc和ImplementableFunc,我们不会在C++里写上它们的实现,因此为了编译通过,也为了可以从C++端直接调用,就需要在生成的代码的时候也同样生成一份默认实现。
在之前的简单类生成代码中,StaticRegisterNativesUMyClass总是空的,在这里UHT为它加上了把函数注册进程序内存的操作。
观察生成的代码可知,其实就分两部分,一是各种Z_辅助方法用来构造出各种UClass*等对象;另一部分是都包含着一两个static对象用来在程序启动的时候驱动登记,继而调用到前者的Z_方法,最终完成注册。