前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >UE4 反射系统详细剖析

UE4 反射系统详细剖析

原创
作者头像
阿苏勒
修改2021-10-19 20:52:03
9.8K0
修改2021-10-19 20:52:03
举报
文章被收录于专栏:阿苏勒的精神小屋

使用UE4的同学一定对"*.generated.h"这个名字不陌生。蓝图、HotReload等功能都依赖于反射机制。本文会从生成代码分析UE4的反射系统是如何实现的。

TOC

本文代码版本为UE4.21。

本文实际上是对照《Inside UE4》知乎专栏,分析源码的学习笔记。想要深入了解的可以去专栏。专栏地址为:

https://zhuanlan.zhihu.com/insideue4

传统反射实现

不少语言都有反射系统。动态语言不说,很多工具都可以实现静态语言的反射。比较有名的就是Qt。

我们可以思考一下如果要我们自己实现一个C++的反射系统,将会如何实现?

当前C++实现反射的方案包括以下流派:

  • :基本思想就是采用宏来代替常规的变量定义。例如:struct Test { DECLARE_STRUCT(Test); DEFINE_FIELD(1, int, a) DEFINE_FIELD(2, int, b) DEFINE_FIELD(3, int, c) DEFINE_METADATA(3) } 自己可以在宏函数中将定义的变量添加至自己的反射系统。undefined 但是这种方案的缺点也是显而易见的,会破坏语法的通用性。要使用你的系统必须使用你的宏,提高了使用成本,也大大降低了代码的兼容性。
  • 模板:总体说也是类似于宏,利用模板在编译起见创建出相应的数据结构。不过在使用过程中需要手动调用函数去注册。
  • 编译器数据分析:实际就是分析pdb文件,得出所有类的信息。缺点是跟编译器强相关,而且编译器版本一更新,有可能生成数据格式会发生变化。使用的人很少。
  • 工具生成代码:这是实用性最高的反射方案,也是Qt和UE4采用的反射方案。 原理是利用特殊的宏来对变量做标记。对C++代码文件进行语法分析,识别出特殊的宏,提取出对应的数据。然后生成代码,在初始化时运行生成的代码,将收集到的数据保存。 可以看一下QT的反射调用方式:class Test: public QObject { Q_OBJECT // 需要自己定义一个只有反射才会用到的属性 Q_PROPERTY(int value READ value WRITE setValue) public: Q_INVOKABLE int value(); Q_INVOKABLE void setValue(int); // 函数前加Q_INVOKABLE即可通过反射调用函数 Q_INVOKABLE void func1(); };

UE4反射类型定义

UE4定义了一系列的宏,来帮助开发者将自定义的字段和函数添加至反射系统。

先来看一段典型的UE4的反射声明代码:

代码语言:txt
复制
// 一定要声明UCLASS
UCLASS()
class MYGAME_API UMyClass : public UObject
{
	GENERATED_BODY()
public:
	// 定义一个可反射的函数
	UFUNCTION(BluprintCallable)
	void MyFunc();
private:
	// 定义一个可反射的变量
	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(AllowPrivateAccess = "true"))
	int MyIntValue;
}

反射类型结构总览

先来看看类图:

UObject全家福类图
UObject全家福类图

可以使用的宏定义:

反射宏名称

作用

UCLASS

告诉UE这个类是一个反射类。类必须派生自UObject

USTRUCT

可以不用派生自UObject。不支持GC,也不能包含函数

UPROPERTY

定义一个反射的变量

UFUNCTION

定义一个反射的函数

UENUM

告诉UE这是一个反射的枚举类。支持enum, enum class, enum namespace

UINTERFACE

定义一个反射接口类,只能包含函数

UMETA

反射的一些元数据定义,可以通过标签定义一些该变量的属性

UPARAM

定义函数的参数属性。主要就是显示名字和Ref属性

UDELEGATE

告诉UE这是一个可反射的delegate(很少用到)

UHT解析原理

具体解析的流程在FHeaderParser::ParseHeader中。采用的是传统的LL分析法手写代码,而并没有采用LR生成代码。好处就是可以灵活地写逻辑。(光这一个文件就1w行。。)

可以稍微看一下解析的核心代码:

代码语言:txt
复制
// 解析代码
ECompilationResult::Type FHeaderParser::ParseHeader(FClasses& AllClasses, FUnrealSourceFile* SourceFile)
{
	// ...
	// 解析一个Statement
	while (CompileStatement(AllClasses, DelegatesToFixup))
	{
		// ...
		StatementsParsed++;
	}
	// ...
}

// 获取并解析一个Token
bool FHeaderParser::CompileStatement(FClasses& AllClasses, TArray<UDelegateFunction*>& DelegatesToFixup)
{
		// Get a token and compile it.
	FToken Token;
	if( !GetToken(Token, true) )
	{
		// End of file.
		return false;
	}
	else if (!CompileDeclaration(AllClasses, DelegatesToFixup, Token))
	{
		FError::Throwf(TEXT("'%s': Bad command or expression"), Token.Identifier );
	}
	return true;
}

// 解析所获取的Token
bool FHeaderParser::CompileDeclaration(FClasses& AllClasses, TArray<UDelegateFunction*>& DelegatesToFixup, FToken& Token)
{
	// ...
	if (Token.Matches(TEXT("UCLASS"), ESearchCase::CaseSensitive))
	{
		// ...
		UClass* Class = CompileClassDeclaration(AllClasses);
		// ...
	}
	if (Token.Matches(TEXT("UINTERFACE"), ESearchCase::CaseSensitive))
	{
		// ...
		CompileInterfaceDeclaration(AllClasses);
		// ...
	}
	// 以及其它所有的宏的匹配。。
}

反射代码生成

第一步解析完所有的反射信息之后,要如何注入到程序中去呢?答案就是:生成代码。

相信有过UE4开发经验的同学都对"*.generated.h"这个名字不陌生。这就是UE4的UHT(Unreal Header Tool)模块扫描之后生成的代码。会生成.h和.cpp两个文件:

  • .generated.h文件:重载各种操作符函数,声明各种构造函数。
  • .gen.cpp文件:单例实现,构造UClass(提取信息并注册)

我们可以通过一个简单的UClass,来查看生成的代码。我们创建一个文件:MyObject.h

代码语言:txt
复制
// MyObject.h
#include "MyObject.generated.h"

UENUM()
enum class EMyEnum
{
    Enm1 = 1,
    Enm2 = 2,
};

USTRUCT()
struct FMyStruct
{
    GENERATED_BODY()

    UPROPERTY()
    float StructProperty;
};

UINTERFACE()
class UMyInterface : public UInterface
{
	GENERATED_BODY()
}

class IMyInterface
{
	GENERATED_BODY()
public:
	UFUNCTION()
	virtual void InterfaceFunction();
}

UCLASS()
class THEMYPROJECT_API UMyObject : public UObject
{
	GENERATED_BODY()

public:
    UFUNCTION()
    void ClassFunction() {};

    UPROPERTY()
    int ClassProperty;
};

这个简单的类,麻雀虽小五脏俱全,包含了UCLASS、USTRUCT、UINTERFACE、UENUM、UFUNCTION、UPROPERTY六种类型。那么反射系统生成出来的代码是怎样的呢?

反射代码注入机制

首先来看MyObject.generated.h。很多行,这里分块阐述。有兴趣的同学可以用上述示例代码来生成反射代码亲自查看。

第一个宏定义如下:

代码语言:txt
复制
#define MyProject_Source_MyProject_MyObject_h_19_GENERATED_BODY \
	friend struct Z_Construct_UScriptStruct_FMyStruct_Statics; \
	THEMYPROJECT_API static class UScriptStruct* StaticStruct();

可以看到宏名构成:

路径名_行号_GENERATE_BODY

我们再来看GENERATE_BODY宏。

代码语言:txt
复制
#define CURRENT_FILE_ID MyProject_Source_MyProject_MyObject_h

#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);

完全展开正是:

MyProject_Source_MyProject_MyObject_h_19_GENERATED_BODY

果不其然!

UE4为每个类生成了一个专门的反射数据收集类,以友元类的方式来访问类信息。

根据行号,可以知道这是FMyStruct类的生成宏代码,也就是USTRUCT的生成宏代码。

类似的,UClass也是一样的路数。利用ReSharper的宏展开功能(强烈推荐ReSharper 2019来开发UE4,会对UE4做专门的提示和补全优化),可以看到我们定义的类,最终被转换为了一系列反射代码。

这里为了阅读流畅性,生成的代码放在附录中。

这里有两点值得注意:

  1. 默认生成的代码,函数名和字段名都以Z_开头。这只是为了在智能提示中排在最下面
  2. 很多宏直接放在了ObjectMacros.h文件中。

好,那生成的类总得运行吧?那会在什么地方调用呢?

答案就是:通过static构造函数来在全局main函数之前,执行反射系统的收集逻辑

关键就在于.gen.cpp代码中的IMPLEMENT_CLASS宏。这个宏展开之后如下:

代码语言:txt
复制
static TClassCompiledInDefer<UMyObject> AutoInitializeUMyObject(L"UMyObject", sizeof(UMyObject), 2973152461);

// TClassCompiledInDefer的构造函数,会把类信息添加到DeferredClassRegistration数组中
TClassCompiledInDefer(const TCHAR* InName, SIZE_T InClassSize, uint32 InCrc)
: FFieldCompiledInInfo(InClassSize, InCrc)
{
	UClassCompiledInDefer(this, InName, InClassSize, InCrc);
}

void UClassCompiledInDefer(FFieldCompiledInInfo* ClassInfo, const TCHAR* Name, SIZE_T ClassSize, uint32 Crc)
{
	// ...
	GetDeferredClassRegistration().Add(ClassInfo);
}

在CoreNative.cpp的StartupModule函数中,会依次处理这些收集到的类信息。

代码语言:txt
复制
TArray<FFieldCompiledInInfo*>& DeferredClassRegistration = GetDeferredClassRegistration();
for (const FFieldCompiledInInfo* Class : DeferredClassRegistration)
{
	UClass* RegisteredClass = Class->Register();
	// ...
}

// Register会调用类的StaticClass函数

下面我们来通过这些代码,依次解读不同反射宏的实现原理。

不同类型生成代码

UCLASS

首先是通过设置友元类的方式来访问原始定义的类,收集原始类的数据

代码语言:txt
复制
friend struct Z_Construct_UClass_UMyObject_Statics;

这个类的定义则放在了MyObject.gen.cpp中。

代码语言:txt
复制
struct Z_Construct_UClass_UMyObject_Statics
{
    static UObject* (*const DependentSingletons[])();
    static const FClassFunctionLinkInfo FuncInfo[];
#if WITH_METADATA
    static const UE4CodeGen_Private::FMetaDataPairParam Class_MetaDataParams[];
#endif
#if WITH_METADATA
    static const UE4CodeGen_Private::FMetaDataPairParam NewProp_ClassProperty_MetaData[];
#endif
    static const UE4CodeGen_Private::FUnsizedIntPropertyParams NewProp_ClassProperty;
    static const UE4CodeGen_Private::FPropertyParamsBase* const PropPointers[];
    static const FCppClassTypeInfoStatic StaticCppClassTypeInfo;
    static const UE4CodeGen_Private::FClassParams ClassParams;
};

这个数据类只是负责收集原始类的信息。注意到全都是const的变量。会通过const提前初始化的特性来直接生成类参数。

例如,收集这个类的属性信息的代码:

代码语言:txt
复制
// ClassProperty变量的信息
// 这里使用了初始化列表来直接对没有虚表的类进行初始化
const UE4CodeGen_Private::FUnsizedIntPropertyParams Z_Construct_UClass_UMyObject_Statics::NewProp_ClassProperty = {
    UE4CodeGen_Private::EPropertyClass::Int, "ClassProperty", RF_Public | RF_Transient | RF_MarkAsNative,
    (EPropertyFlags)0x0010000000000000, 1, nullptr, STRUCT_OFFSET(UMyObject, ClassProperty), METADATA_PARAMS(
        Z_Construct_UClass_UMyObject_Statics::NewProp_ClassProperty_MetaData,
        ARRAY_COUNT(Z_Construct_UClass_UMyObject_Statics::NewProp_ClassProperty_MetaData))
};
// 只有ClassProperty一个变量,因此数组中只有一个值
const UE4CodeGen_Private::FPropertyParamsBase* const Z_Construct_UClass_UMyObject_Statics::PropPointers[] = {
    (const UE4CodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_UMyObject_Statics::NewProp_ClassProperty,
};

所有类的关键信息,都会通过构造ClassParam来生成。

代码语言:txt
复制
const UE4CodeGen_Private::FClassParams Z_Construct_UClass_UMyObject_Statics::ClassParams = {
    &UMyObject::StaticClass, // ClassNoregisterFunc
    DependentSingletons, // DependencySingletonFuncArray 
    ARRAY_COUNT(DependentSingletons), // NumDependencySingletons
    0x001000A0u, // ClassFlags
    FuncInfo, // FunctionLinkArray
    ARRAY_COUNT(FuncInfo), // NumFunctions
    Z_Construct_UClass_UMyObject_Statics::PropPointers, // PropertyArray
    ARRAY_COUNT(Z_Construct_UClass_UMyObject_Statics::PropPointers), // NumProperties
    nullptr, // ClassConfigNameUTF8
    &StaticCppClassTypeInfo, // CppClassInfo
    nullptr, // ImplementedInterfaceArray
    0, // NumImplementedInterfaces
    Z_Construct_UClass_UMyObject_Statics::Class_MetaDataParams, // MetaDataArray
    (sizeof(ArrayCountHelper(Z_Construct_UClass_UMyObject_Statics::Class_MetaDataParams)) - 1), // NumMetaData
};

这些类信息,会通过调用StaticClass传给反射系统

代码语言:txt
复制
inline static UClass* StaticClass() { return GetPrivateStaticClass(); }

// 这里会将各种类的信息参数传给反射系统。
UClass* UMyObject::GetPrivateStaticClass()
{
    static UClass* PrivateStaticClass = 0;
    if (!PrivateStaticClass)
    {
        GetPrivateStaticClassBody(StaticPackage(), // PackageName
                                  (TCHAR*)L"UMyObject" + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0), // Name
                                  PrivateStaticClass, // ReturnClass
                                  StaticRegisterNativesUMyObject, // RegisterNativeFunc
                                  sizeof(UMyObject), // InSize
                                  (EClassFlags)UMyObject::StaticClassFlags, // InClassFlags
                                  UMyObject::StaticClassCastFlags(), // InClassCastFlags
                                  UMyObject::StaticConfigName(), // InConfigName
                                  (UClass::ClassConstructorType)InternalConstructor<UMyObject>, // InClassConstructor
                                  (UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<UMyObject>,
                                  // InClassVTableHelperCtorCaller
                                  &UMyObject::AddReferencedObjects, // InClassAddReferencedObjects
                                  &UMyObject::Super::StaticClass, // InSuperClassFn
                                  &UMyObject::WithinClass::StaticClass); // InWithinClassFn
    }
    return PrivateStaticClass;
};

(UClass是最典型的反射,因此将代码放在这里详细讲。后续不会那么详细。PS:相信后续详细也没有同学想再认真看吧。。)

UPROPERTY

先来看看定义在UClass中的属性是如何收集的。

在上面代码中,我们定义了一个属性:int ClassProperty

生成的代码都放在.gen.cpp文件中。

仔细观察之前UClass的数据收集类Z_Construct_UClass_UMyObject_Statics,可以看到定义了属性信息:

代码语言:txt
复制
struct Z_Construct_UClass_UMyObject_Statics
{
    // ...
    static const UE4CodeGen_Private::FUnsizedIntPropertyParams NewProp_ClassProperty;	// 为ClassProperty属性生成的信息
    static const UE4CodeGen_Private::FPropertyParamsBase* const PropPointers[];	// 所有属性的信息数组
    // ...
};

这里会对每个属性单独生成单独的信息。这里只定义了一个属性,因此只有一条信息。

代码语言:txt
复制
const UE4CodeGen_Private::FUnsizedIntPropertyParams Z_Construct_UClass_UMyObject_Statics::NewProp_ClassProperty = {
        UE4CodeGen_Private::EPropertyClass::Int,    // NameUTF8
        "ClassProperty",    // RepNotifyFuncUTF8
        RF_Public | RF_Transient | RF_MarkAsNative, // PropertyFlags
        (EPropertyFlags) 0x0010000000000000,    // Flags
        1,  // ObjectFlags
        nullptr,    // ArrayDim
        STRUCT_OFFSET(UMyObject, ClassProperty),    // Offset
        METADATA_PARAMS(
                Z_Construct_UClass_UMyObject_Statics::NewProp_ClassProperty_MetaData,
                ARRAY_COUNT(Z_Construct_UClass_UMyObject_Statics::NewProp_ClassProperty_MetaData))  // MetaDataArray and NumMetaData
};

然后添加到数组中:

代码语言:txt
复制
const UE4CodeGen_Private::FPropertyParamsBase* const Z_Construct_UClass_UMyObject_Statics::PropPointers[] = {
    (const UE4CodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_UMyObject_Statics::NewProp_ClassProperty,
};

这样就完成了UPROPERTY的收集

UFUNCTION

UFUNCTION的收集比UPROPERTY稍微复杂一点,因为多出了两点:

  1. 反射函数调用
  2. 函数形参收集

下面继续依次来看这两点:

  1. 反射函数调用: 会为每个函数生成一个包装函数: static void execClassFunction(UObject* Context, FFrame& Stack, void* const Z_Param__Result) { Stack.Code += !!Stack.Code;; { ;; ((ThisClass*)(Context))->ClassFunction(); }; }并会将所有生成的包装函数添加到函数列表:void UMyObject::StaticRegisterNativesUMyObject() { UClass* Class = UMyObject::StaticClass(); // 这里只定义了一个函数,因此数组中只有一个 static const FNameNativePtrPair Funcs[] = { {"ClassFunction", &UMyObject::execClassFunction}, }; FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, ARRAY_COUNT(Funcs)); }
  2. 函数参数收集 会为每个函数的返回值和所有形参生成参数信息。这里返回值添加至了参数列表,不过有一个特定的名称ReturnValue:// 生成返回值参数信息 const UE4CodeGen_Private::FUnsizedIntPropertyParams Z_Construct_UFunction_UMyObject_ClassFunction_Statics:: NewProp_ReturnValue = { UE4CodeGen_Private::EPropertyClass::Int, "ReturnValue", RF_Public | RF_Transient | RF_MarkAsNative, (EPropertyFlags)0x0010000000000580, 1, nullptr, STRUCT_OFFSET(MyObject_eventClassFunction_Parms, ReturnValue), METADATA_PARAMS(nullptr, 0) }; // 生成形参参数信息 const UE4CodeGen_Private::FUnsizedIntPropertyParams Z_Construct_UFunction_UMyObject_ClassFunction_Statics:: NewProp_IntParam = { UE4CodeGen_Private::EPropertyClass::Int, "IntParam", RF_Public | RF_Transient | RF_MarkAsNative, (EPropertyFlags)0x0010000000000080, 1, nullptr, STRUCT_OFFSET(MyObject_eventClassFunction_Parms, IntParam), METADATA_PARAMS(nullptr, 0) }; // 将生成的信息添加至参数数组 const UE4CodeGen_Private::FPropertyParamsBase* const Z_Construct_UFunction_UMyObject_ClassFunction_Statics::PropPointers [] = { (const UE4CodeGen_Private::FPropertyParamsBase*)&Z_Construct_UFunction_UMyObject_ClassFunction_Statics:: NewProp_ReturnValue, (const UE4CodeGen_Private::FPropertyParamsBase*)&Z_Construct_UFunction_UMyObject_ClassFunction_Statics:: NewProp_IntParam, };

生成的参数数组会放在FuncParam中。最终,也是通过UClass的收集数据类将信息传给反射系统。

代码语言:txt
复制
struct Z_Construct_UClass_UMyObject_Statics
{
    // ...
    static const FClassFunctionLinkInfo FuncInfo[];
    // ...
};

USTRUCT

收集的原理和UCLASS大致是一样的。区别主要在于两点:

  1. USTRUCT不属于UClass,GENERATED_BODY不会生成那么一大堆额外的函数和变量,只会注入一个友元类和一个StaticStruct函数。
  2. USTRUCT不能包含函数,因此收集的数据中不包含函数信息。

GENERATED_BODY注入的代码如下:

代码语言:txt
复制
struct FMyStruct
{
    friend struct Z_Construct_UScriptStruct_FMyStruct_Statics;
    static class UScriptStruct* StaticStruct();;

    float StructProperty;
};

生成的数据收集类结构如下:

代码语言:txt
复制
struct Z_Construct_UScriptStruct_FMyStruct_Statics
{
#if WITH_METADATA
    static const UE4CodeGen_Private::FMetaDataPairParam Struct_MetaDataParams[];
#endif
    static void* NewStructOps();
#if WITH_METADATA
    static const UE4CodeGen_Private::FMetaDataPairParam NewProp_StructProperty_MetaData[];
#endif
    static const UE4CodeGen_Private::FFloatPropertyParams NewProp_StructProperty;
    static const UE4CodeGen_Private::FPropertyParamsBase* const PropPointers[];
    static const UE4CodeGen_Private::FStructParams ReturnStructParams;
};

可以看到,与UClass相比,没有FuncInfo,也没有StaticCppClassTypeInfo类信息。

其它的机制,都是相同的。同样会通过StaticStruct函数来将信息传递给反射系统

UINTERFACE

UINTERFACE定义时就比较不一样,需要定义两个类,一个是UMyInterface类,继承UInterface,其中什么数据都没有。另一个是IMyInterface类,什么都不继承。

对于反射系统来说,一个接口和一个类的区别,主要有三点:

  1. 接口类是abstract类不能实例化。这点可以通过设置ClassFlag来设置。
  2. 里面的函数必须实现。通过强制virtual关键字来实现。
  3. 接口中不能包含属性,这点会在UHT扫描时检查。

UMyInterface类生成的代码与UCLASS基本是一样的,只是声明了一个反射类。区别只是设置了ClassFlag为CLASS_Abstract和CLASS_Interface。

代码语言:txt
复制
enum { StaticClassFlags=(CLASS_Abstract | CLASS_Interface | CLASS_Intrinsic) };

至于另一个类IMyInterface,其实也没有做些什么特殊的处理。只是将包装的函数实现放在了这个类中:

代码语言:txt
复制
class IMyInterface
{
    // ...
    static void execInterfaceFunction(UObject* Context, FFrame& Stack, void* const Z_Param__Result)
    {
        Stack.Code += !!Stack.Code;;
        {
            ;;
            ((ThisClass*)(Context))->InterfaceFunction();
        };
    }
	// ...
}

而UMyInterface类构造传递的函数则是传递了IMyInterface类中定义的函数。

代码语言:txt
复制
void UMyInterface::StaticRegisterNativesUMyInterface()
{
    UClass* Class = UMyInterface::StaticClass();
    static const FNameNativePtrPair Funcs[] = {
        {"InterfaceFunction", &IMyInterface::execInterfaceFunction},	// 这里传递的函数是IMyInterface中的函数
    };
    FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, ARRAY_COUNT(Funcs));
}

这里不禁要思考:为什么要用两个类来实现呢?

因为接口是存在多继承的,一个类有可能继承多个接口。如果每个接口都是UObject,会出现菱形继承的情况。而菱形继承

会大大扩张虚表的大小,而且会造成二义性,调用基类的函数需要显示声明,这肯定是不现实的。

这个时候,UINTERFACE用两个类来实现,就可以避免菱形继承的问题。外部类如果要继承接口的话,只能继承IMyInterface类。

另外这里还有一点值得注意:如果一个类继承了UINTERFACE:

在InterfaceParams里面会传入多重继承的指针偏移offset(通过VTABLE_OFFSET获取)。

代码语言:txt
复制
    static const UE4CodeGen_Private::FImplementedInterfaceParams InterfaceParams[]= 
    {
        {
            Z_Construct_UClass_UMyInterface_NoRegister,//构造UMyInterface所属的UClass*函数指针
            (int32)VTABLE_OFFSET(UMyClass, IMyInterface),//多重继承的指针偏移
            false   //是否是在蓝图实现
        }
    };

因为接口是使用多继承,因此需要通过指针偏移,来根据**Obj + Offset**来获取接口地址调用接口函数

UENUM

最后是枚举的收集。这个不需要添加GENERATED_BODY宏,所有的代码都在.gen.cpp文件中生成。

会在构造的时候生成所有枚举值:

代码语言:txt
复制
static const UE4CodeGen_Private::FEnumeratorParam Enumerators[] = {
	{"EMyEnum::Enm1", (int64)EMyEnum::Enm1},
	{"EMyEnum::Enm2", (int64)EMyEnum::Enm2},
};

同其他类型一样,也是通过构造Param传递给反射系统:

代码语言:txt
复制
static const UE4CodeGen_Private::FEnumParams EnumParams = {
    (UObject*(*)())Z_Construct_UPackage__Script_MyProject,
    UE4CodeGen_Private::EDynamicType::NotDynamic,
    "EMyEnum",
    RF_Public | RF_Transient | RF_MarkAsNative,
    nullptr,
    (uint8)UEnum::ECppForm::EnumClass,
    "EMyEnum",
    Enumerators,
    ARRAY_COUNT(Enumerators),
    Enum_MetaDataParams, (sizeof(ArrayCountHelper(Enum_MetaDataParams)) - 1),
};
UE4CodeGen_Private::ConstructUEnum(ReturnEnum, EnumParams);

UE4反射系统存储结构及运行机制

上面我们非常详细地分析了各种反射宏定义所生成的代码和数据收集流程。

前文说过,会在CoreNative.cpp的StartupModule函数中真正处理所有类的注册。

现在我们已经拿到了这些类数据了,那么怎么存储呢?

StaticClass数据收集

关键在于GetPrivateStaticClassBody函数。这个函数会在Register调用StaticClass的时候调用,主要会做以下几件事:

  1. 分配内存ReturnClass = (UClass*)GUObjectAllocator.AllocateUObject(sizeof(UClass), alignof(UClass), true); ReturnClass = ::new (ReturnClass) UClass ( EC_StaticConstructor, Name, InSize, InClassFlags, InClassCastFlags, InConfigName, EObjectFlags(RF_Public | RF_Standalone | RF_Transient | RF_MarkAsNative | RF_MarkAsRootSet), InClassConstructor, InClassVTableHelperCtorCaller, InClassAddReferencedObjects ); InitializePrivateStaticClass( InSuperClassFn(), // 这里会触发Super::StaticClass()静态构造 ReturnClass, InWithinClassFn(), // 这里会触发WithinClass::StaticClass()静态构造 PackageName, Name );
  2. 递归调用依赖的类的StaticClass构造
  3. 简单记录信息到中间静态Map和中间静态链表中。(Map是为了查找方便,链表是为了保证注册顺序)(这其中的信息会在所有Register完成之后,再使用添加到真正的引擎查找使用的全局Map中)/** Enqueue the registration for this object. */ void UObjectBase::Register(const TCHAR* PackageName,const TCHAR* InName) { // 中间Map TMap<UObjectBase*, FPendingRegistrantInfo>& PendingRegistrants = FPendingRegistrantInfo::GetMap();
代码语言:txt
复制
FPendingRegistrant\* PendingRegistration = new FPendingRegistrant(this);
代码语言:txt
复制
PendingRegistrants.Add(this, FPendingRegistrantInfo(InName, PackageName));
代码语言:txt
复制
if(GLastPendingRegistrant)
代码语言:txt
复制
{
代码语言:txt
复制
	GLastPendingRegistrant->NextAutoRegister = PendingRegistration;
代码语言:txt
复制
}
代码语言:txt
复制
else
代码语言:txt
复制
{
代码语言:txt
复制
	check(!GFirstPendingRegistrant);
代码语言:txt
复制
	GFirstPendingRegistrant = PendingRegistration;
代码语言:txt
复制
}
代码语言:txt
复制
GLastPendingRegistrant = PendingRegistration;

}

  1. 注册所有类的UFUNCTION。(即运行.gen.cpp中生成的StaticRegisterNativesUMyObject函数)。实质是在UClass的变量NativeFunctionLookupTable中,添加函数指针。

在所有类Register之后,所有数据信息都已经收集完毕。接下来就是在引擎的初始化时去使用这些收集的信息构造引擎中真正存储的结构树。

核心代码在这里,通过一个while循环来真正构造结构树。注意这里的循环条件是计算Num,因为使用的这些数组是有可能变化的,每次循环都需要重新计算一次Num。

代码语言:txt
复制
void ProcessNewlyLoadedUObjects() {
	//...
	bool bNewUObjects = false;
	while( GFirstPendingRegistrant || DeferredCompiledInRegistration.Num() || DeferredCompiledInStructRegistration.Num() || DeferredCompiledInEnumRegistration.Num() )
	{
		bNewUObjects = true;
		UObjectProcessRegistrants();	// 处理所有UObject
		UObjectLoadAllCompiledInStructs();	// 处理所有Enum和Struct
		UObjectLoadAllCompiledInDefaultProperties();	// 构造CDO(Class Default Object)
	}
	//...
}

内部的结构会使用一个全局数组来存储所有Object。并会用一个UObjectHash结构来存储Name到UObject指针的关联关系。

用Hash支持快速查找

这个UObjectHash结构也是专门优化过的特殊Hash结构。

使用了多个TMap来加速查找过程

代码语言:txt
复制
// UObject哈希结构
class FUObjectHashTables
{
	// ...
	/** Hash sets */
	TMap<int32, FHashBucket> Hash;
	TMultiMap<int32, class UObjectBase*> HashOuter;

	/** Map of object to their outers, used to avoid an object iterator to find such things. **/
	TMap<UObjectBase*, FHashBucket> ObjectOuterMap;
	TMap<UClass*, TSet<UObjectBase*> > ClassToObjectListMap;
	TMap<UClass*, TSet<UClass*> > ClassToChildListMap;
	// ...
}

注意一下Hash和HashOuter的Key,是特殊计算出来的int值。利用了FName的全局唯一性,保证每个FName的hash值不一样。

代码语言:txt
复制
// 设置Number的核心代码
#define NAME_INTERNAL_TO_EXTERNAL(x) (x - 1)
#define NAME_EXTERNAL_TO_INTERNAL(x) (x + 1)

// 计算hash的函数
static FORCEINLINE int32 GetObjectHash(FName ObjName)
{
	return (ObjName.GetComparisonIndex() ^ ObjName.GetNumber());
}

FHashBucket是一个用于处理哈希冲突的类。

总结

本文从常用的反射实现方法入手,详细分析了UE4通过宏定义生成反射信息的机制,并详细分析了UE4各类型的反射生成代码,以及UE4内部是如何使用反射信息的。

对UE4反射信息机制的了解,有助于我们对UE4的特化C++有更深的认识。

笔者在项目中也通过UE4的反射系统,搭配Emmylua语法,实现了Lua中的UE4函数自动补全。有兴趣的同学可以参考这篇文章:

在Lua中实现对UE4 C++代码的自动补全

附录:展开生成代码

这里隐藏了所有#pragma warning的代码。所有宏替换为了展开代码。

UCLASS生成代码

原始代码:

代码语言:txt
复制
#include "CoreMinimal.h"
#include "MyObject.generated.h"

UCLASS()
class THEMYPROJECT_API UMyObject : public UObject
{
    GENERATED_BODY()

public:
    UFUNCTION()
    int ClassFunction(int IntParam) { };

    UPROPERTY()
    int ClassProperty;
};

展开GENERATED_BODY之后的代码:

代码语言:txt
复制
#include "CoreMinimal.h"

#include "UObject/ObjectMacros.h"
#include "UObject/ScriptMacros.h"

class THEMYPROJECT_API UMyObject : public UObject
{
public:
    static void execClassFunction(UObject* Context, FFrame& Stack, void* const Z_Param__Result)
    {
        UIntProperty::TCppType Z_Param_IntParam = UIntProperty::GetDefaultPropertyValue();
        Stack.StepCompiledIn<UIntProperty>(&Z_Param_IntParam);;
        Stack.Code += !!Stack.Code;;
        {
            ;;
            *(int32*)Z_Param__Result = ((ThisClass*)(Context))->ClassFunction(Z_Param_IntParam);
        };
    }

private:
    static void StaticRegisterNativesUMyObject();
    friend struct Z_Construct_UClass_UMyObject_Statics;
public:
private:
    UMyObject& operator=(UMyObject&&);
    UMyObject& operator=(const UMyObject&);
    static UClass* GetPrivateStaticClass();
public:
    enum { StaticClassFlags=(0 | CLASS_Intrinsic) };

    typedef UObject Super;
    typedef UMyObject ThisClass;
    inline static UClass* StaticClass() { return GetPrivateStaticClass(); }
    inline static const TCHAR* StaticPackage() { return L"/Script/MyProject"; }
    inline static EClassCastFlags StaticClassCastFlags() { return CASTCLASS_None; }

    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);
    }

    inline void* operator new(const size_t InSize, EInternal* InMem) { return (void*)InMem; }
    friend FArchive& operator<<(FArchive& Ar, UMyObject*& Res) { return Ar << (UObject*&)Res; }
    friend void operator<<(FStructuredArchive::FSlot InSlot, UMyObject*& Res) { InSlot << (UObject*&)Res; }

    UMyObject(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer)
    {
    };
private:
    UMyObject(UMyObject&&);
    UMyObject(const UMyObject&);
public:
    UMyObject(FVTableHelper& Helper);;
    static UObject* __VTableCtorCaller(FVTableHelper& Helper) { return nullptr; };
    static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())UMyObject(X); }
private:

public:
    int ClassFunction(int IntParam)
    {
        return 0;
    };

    int ClassProperty;
};

.gen.cpp文件:

代码语言:txt
复制
#include "UObject/GeneratedCppIncludes.h"
#include "MyProject/MyObject.h"

void EmptyLinkFunctionForGeneratedCodeMyObject()
{
}

// Cross Module References
UClass* Z_Construct_UClass_UMyObject_NoRegister();
UClass* Z_Construct_UClass_UMyObject();
UClass* Z_Construct_UClass_UObject();
UPackage* Z_Construct_UPackage__Script_MyProject();
UFunction* Z_Construct_UFunction_UMyObject_ClassFunction();
// End Cross Module References
void UMyObject::StaticRegisterNativesUMyObject()
{
    UClass* Class = UMyObject::StaticClass();
    static const FNameNativePtrPair Funcs[] = {
        {"ClassFunction", &UMyObject::execClassFunction},
    };
    FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, ARRAY_COUNT(Funcs));
}

struct Z_Construct_UFunction_UMyObject_ClassFunction_Statics
{
    struct MyObject_eventClassFunction_Parms
    {
        int32 IntParam;
        int32 ReturnValue;
    };

    static const UE4CodeGen_Private::FUnsizedIntPropertyParams NewProp_ReturnValue;
    static const UE4CodeGen_Private::FUnsizedIntPropertyParams NewProp_IntParam;
    static const UE4CodeGen_Private::FPropertyParamsBase* const PropPointers[];
#if WITH_METADATA
    static const UE4CodeGen_Private::FMetaDataPairParam Function_MetaDataParams[];
#endif
    static const UE4CodeGen_Private::FFunctionParams FuncParams;
};

const UE4CodeGen_Private::FUnsizedIntPropertyParams Z_Construct_UFunction_UMyObject_ClassFunction_Statics::
    NewProp_ReturnValue = {
        UE4CodeGen_Private::EPropertyClass::Int, "ReturnValue", RF_Public | RF_Transient | RF_MarkAsNative,
        (EPropertyFlags)0x0010000000000580, 1, nullptr, STRUCT_OFFSET(MyObject_eventClassFunction_Parms, ReturnValue),
        METADATA_PARAMS(nullptr, 0)
    };
const UE4CodeGen_Private::FUnsizedIntPropertyParams Z_Construct_UFunction_UMyObject_ClassFunction_Statics::
    NewProp_IntParam = {
        UE4CodeGen_Private::EPropertyClass::Int, "IntParam", RF_Public | RF_Transient | RF_MarkAsNative,
        (EPropertyFlags)0x0010000000000080, 1, nullptr, STRUCT_OFFSET(MyObject_eventClassFunction_Parms, IntParam),
        METADATA_PARAMS(nullptr, 0)
    };
const UE4CodeGen_Private::FPropertyParamsBase* const Z_Construct_UFunction_UMyObject_ClassFunction_Statics::PropPointers
    [] = {
        (const UE4CodeGen_Private::FPropertyParamsBase*)&Z_Construct_UFunction_UMyObject_ClassFunction_Statics::
        NewProp_ReturnValue,
        (const UE4CodeGen_Private::FPropertyParamsBase*)&Z_Construct_UFunction_UMyObject_ClassFunction_Statics::
        NewProp_IntParam,
    };
#if WITH_METADATA
const UE4CodeGen_Private::FMetaDataPairParam Z_Construct_UFunction_UMyObject_ClassFunction_Statics::
    Function_MetaDataParams[] = {
        {"ModuleRelativePath", "MyObject.h"},
    };
#endif
const UE4CodeGen_Private::FFunctionParams Z_Construct_UFunction_UMyObject_ClassFunction_Statics::FuncParams = {
    (UObject*(*)())Z_Construct_UClass_UMyObject, "ClassFunction", RF_Public | RF_Transient | RF_MarkAsNative, nullptr,
    (EFunctionFlags)0x00020401, sizeof(MyObject_eventClassFunction_Parms),
    Z_Construct_UFunction_UMyObject_ClassFunction_Statics::PropPointers,
    ARRAY_COUNT(Z_Construct_UFunction_UMyObject_ClassFunction_Statics::PropPointers), 0, 0, METADATA_PARAMS(
        Z_Construct_UFunction_UMyObject_ClassFunction_Statics::Function_MetaDataParams,
        ARRAY_COUNT(Z_Construct_UFunction_UMyObject_ClassFunction_Statics::Function_MetaDataParams))
};

UFunction* Z_Construct_UFunction_UMyObject_ClassFunction()
{
    static UFunction* ReturnFunction = nullptr;
    if (!ReturnFunction)
    {
        UE4CodeGen_Private::ConstructUFunction(ReturnFunction,
                                               Z_Construct_UFunction_UMyObject_ClassFunction_Statics::FuncParams);
    }
    return ReturnFunction;
}

UClass* Z_Construct_UClass_UMyObject_NoRegister()
{
    return UMyObject::StaticClass();
}

struct Z_Construct_UClass_UMyObject_Statics
{
    static UObject* (*const DependentSingletons[])();
    static const FClassFunctionLinkInfo FuncInfo[];
#if WITH_METADATA
    static const UE4CodeGen_Private::FMetaDataPairParam Class_MetaDataParams[];
#endif
#if WITH_METADATA
    static const UE4CodeGen_Private::FMetaDataPairParam NewProp_ClassProperty_MetaData[];
#endif
    static const UE4CodeGen_Private::FUnsizedIntPropertyParams NewProp_ClassProperty;
    static const UE4CodeGen_Private::FPropertyParamsBase* const PropPointers[];
    static const FCppClassTypeInfoStatic StaticCppClassTypeInfo;
    static const UE4CodeGen_Private::FClassParams ClassParams;
};

UObject* (*const Z_Construct_UClass_UMyObject_Statics::DependentSingletons[])() = {
    (UObject* (*)())Z_Construct_UClass_UObject,
    (UObject* (*)())Z_Construct_UPackage__Script_MyProject,
};
const FClassFunctionLinkInfo Z_Construct_UClass_UMyObject_Statics::FuncInfo[] = {
    {&Z_Construct_UFunction_UMyObject_ClassFunction, "ClassFunction"}, // 531444507
};
#if WITH_METADATA
const UE4CodeGen_Private::FMetaDataPairParam Z_Construct_UClass_UMyObject_Statics::Class_MetaDataParams[] = {
    {"IncludePath", "MyObject.h"},
    {"ModuleRelativePath", "MyObject.h"},
};
#endif
#if WITH_METADATA
const UE4CodeGen_Private::FMetaDataPairParam Z_Construct_UClass_UMyObject_Statics::NewProp_ClassProperty_MetaData[] = {
    {"ModuleRelativePath", "MyObject.h"},
};
#endif
const UE4CodeGen_Private::FUnsizedIntPropertyParams Z_Construct_UClass_UMyObject_Statics::NewProp_ClassProperty = {
    UE4CodeGen_Private::EPropertyClass::Int, "ClassProperty", RF_Public | RF_Transient | RF_MarkAsNative,
    (EPropertyFlags)0x0010000000000000, 1, nullptr, STRUCT_OFFSET(UMyObject, ClassProperty), METADATA_PARAMS(
        Z_Construct_UClass_UMyObject_Statics::NewProp_ClassProperty_MetaData,
        ARRAY_COUNT(Z_Construct_UClass_UMyObject_Statics::NewProp_ClassProperty_MetaData))
};
const UE4CodeGen_Private::FPropertyParamsBase* const Z_Construct_UClass_UMyObject_Statics::PropPointers[] = {
    (const UE4CodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_UMyObject_Statics::NewProp_ClassProperty,
};
const FCppClassTypeInfoStatic Z_Construct_UClass_UMyObject_Statics::StaticCppClassTypeInfo = {
    TCppClassTypeTraits<UMyObject>::IsAbstract,
};
const UE4CodeGen_Private::FClassParams Z_Construct_UClass_UMyObject_Statics::ClassParams = {
    &UMyObject::StaticClass,
    DependentSingletons, ARRAY_COUNT(DependentSingletons),
    0x001000A0u,
    FuncInfo, ARRAY_COUNT(FuncInfo),
    Z_Construct_UClass_UMyObject_Statics::PropPointers, ARRAY_COUNT(Z_Construct_UClass_UMyObject_Statics::PropPointers),
    nullptr,
    &StaticCppClassTypeInfo,
    nullptr, 0,
    METADATA_PARAMS(Z_Construct_UClass_UMyObject_Statics::Class_MetaDataParams,
                    ARRAY_COUNT(Z_Construct_UClass_UMyObject_Statics::Class_MetaDataParams))
};

UClass* Z_Construct_UClass_UMyObject()
{
    static UClass* OuterClass = nullptr;
    if (!OuterClass)
    {
        UE4CodeGen_Private::ConstructUClass(OuterClass, Z_Construct_UClass_UMyObject_Statics::ClassParams);
    }
    return OuterClass;
}

static TClassCompiledInDefer<UMyObject> AutoInitializeUMyObject(L"UMyObject", sizeof(UMyObject), 3193407229);

UClass* UMyObject::GetPrivateStaticClass()
{
    static UClass* PrivateStaticClass = 0;
    if (!PrivateStaticClass)
    {
        GetPrivateStaticClassBody(StaticPackage(),
                                  (TCHAR*)L"UMyObject" + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0),
                                  PrivateStaticClass, StaticRegisterNativesUMyObject, sizeof(UMyObject),
                                  (EClassFlags)UMyObject::StaticClassFlags, UMyObject::StaticClassCastFlags(),
                                  UMyObject::StaticConfigName(),
                                  (UClass::ClassConstructorType)InternalConstructor<UMyObject>,
                                  (UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<UMyObject>,
                                  &UMyObject::AddReferencedObjects, &UMyObject::Super::StaticClass,
                                  &UMyObject::WithinClass::StaticClass);
    }
    return PrivateStaticClass;
};
static FCompiledInDefer Z_CompiledInDefer_UClass_UMyObject(Z_Construct_UClass_UMyObject, &UMyObject::StaticClass,
                                                           TEXT("/Script/MyProject"), TEXT("UMyObject"), false,
                                                           nullptr, nullptr, nullptr);

UMyObject::UMyObject(FVTableHelper& Helper) : Super(Helper)
{
};;

USTRUCT生成代码

原始代码:

代码语言:txt
复制
#include "CoreMinimal.h"
#include "MyObject.generated.h"

USTRUCT()
struct FMyStruct
{
    GENERATED_BODY()

    UPROPERTY()
    int StructProperty;
};

展开GENERATED_BODY之后的代码:

代码语言:txt
复制
#include "CoreMinimal.h"

#include "UObject/ObjectMacros.h"
#include "UObject/ScriptMacros.h"

struct FMyStruct
{
    friend struct Z_Construct_UScriptStruct_FMyStruct_Statics;
    static class UScriptStruct* StaticStruct();;

    int StructProperty;
};

.gen.cpp生成代码:

代码语言:txt
复制
#include "UObject/GeneratedCppIncludes.h"
#include "MyProject/MyObject.h"

void EmptyLinkFunctionForGeneratedCodeMyObject()
{
}

// Cross Module References
THEMYPROJECT_API UScriptStruct* Z_Construct_UScriptStruct_FMyStruct();
UPackage* Z_Construct_UPackage__Script_MyProject();
// End Cross Module References
class UScriptStruct* FMyStruct::StaticStruct()
{
    static class UScriptStruct* Singleton = NULL;
    if (!Singleton)
    {
        extern THEMYPROJECT_API uint32 Get_Z_Construct_UScriptStruct_FMyStruct_CRC();
        Singleton = GetStaticStruct(Z_Construct_UScriptStruct_FMyStruct, Z_Construct_UPackage__Script_MyProject(),
                                    TEXT("MyStruct"), sizeof(FMyStruct), Get_Z_Construct_UScriptStruct_FMyStruct_CRC());
    }
    return Singleton;
}

static FCompiledInDeferStruct Z_CompiledInDeferStruct_UScriptStruct_FMyStruct(
    FMyStruct::StaticStruct, TEXT("/Script/MyProject"), TEXT("MyStruct"), false, nullptr, nullptr);

static struct FScriptStruct_MyProject_StaticRegisterNativesFMyStruct
{
    FScriptStruct_MyProject_StaticRegisterNativesFMyStruct()
    {
        UScriptStruct::DeferCppStructOps(FName(TEXT("MyStruct")), new UScriptStruct::TCppStructOps<FMyStruct>);
    }
} ScriptStruct_MyProject_StaticRegisterNativesFMyStruct;

struct Z_Construct_UScriptStruct_FMyStruct_Statics
{
#if WITH_METADATA
    static const UE4CodeGen_Private::FMetaDataPairParam Struct_MetaDataParams[];
#endif
    static void* NewStructOps();
#if WITH_METADATA
    static const UE4CodeGen_Private::FMetaDataPairParam NewProp_StructProperty_MetaData[];
#endif
    static const UE4CodeGen_Private::FUnsizedIntPropertyParams NewProp_StructProperty;
    static const UE4CodeGen_Private::FPropertyParamsBase* const PropPointers[];
    static const UE4CodeGen_Private::FStructParams ReturnStructParams;
};
#if WITH_METADATA
const UE4CodeGen_Private::FMetaDataPairParam Z_Construct_UScriptStruct_FMyStruct_Statics::Struct_MetaDataParams[] = {
    {"ModuleRelativePath", "MyObject.h"},
};
#endif
void* Z_Construct_UScriptStruct_FMyStruct_Statics::NewStructOps()
{
    return (UScriptStruct::ICppStructOps*)new UScriptStruct::TCppStructOps<FMyStruct>();
}
#if WITH_METADATA
const UE4CodeGen_Private::FMetaDataPairParam Z_Construct_UScriptStruct_FMyStruct_Statics::
    NewProp_StructProperty_MetaData[] = {
        {"ModuleRelativePath", "MyObject.h"},
    };
#endif
const UE4CodeGen_Private::FUnsizedIntPropertyParams Z_Construct_UScriptStruct_FMyStruct_Statics::NewProp_StructProperty
    = {
        UE4CodeGen_Private::EPropertyClass::Int, "StructProperty", RF_Public | RF_Transient | RF_MarkAsNative,
        (EPropertyFlags)0x0010000000000000, 1, nullptr, STRUCT_OFFSET(FMyStruct, StructProperty), METADATA_PARAMS(
            Z_Construct_UScriptStruct_FMyStruct_Statics::NewProp_StructProperty_MetaData,
            ARRAY_COUNT(Z_Construct_UScriptStruct_FMyStruct_Statics::NewProp_StructProperty_MetaData))
    };
const UE4CodeGen_Private::FPropertyParamsBase* const Z_Construct_UScriptStruct_FMyStruct_Statics::PropPointers[] = {
    (const UE4CodeGen_Private::FPropertyParamsBase*)&Z_Construct_UScriptStruct_FMyStruct_Statics::
    NewProp_StructProperty,
};
const UE4CodeGen_Private::FStructParams Z_Construct_UScriptStruct_FMyStruct_Statics::ReturnStructParams = {
    (UObject* (*)())Z_Construct_UPackage__Script_MyProject,
    nullptr,
    &NewStructOps,
    "MyStruct",
    RF_Public | RF_Transient | RF_MarkAsNative,
    EStructFlags(0x00000001),
    sizeof(FMyStruct),
    alignof(FMyStruct),
    Z_Construct_UScriptStruct_FMyStruct_Statics::PropPointers,
    ARRAY_COUNT(Z_Construct_UScriptStruct_FMyStruct_Statics::PropPointers),
    METADATA_PARAMS(Z_Construct_UScriptStruct_FMyStruct_Statics::Struct_MetaDataParams,
                    ARRAY_COUNT(Z_Construct_UScriptStruct_FMyStruct_Statics::Struct_MetaDataParams))
};

UScriptStruct* Z_Construct_UScriptStruct_FMyStruct()
{
#if WITH_HOT_RELOAD
    extern uint32 Get_Z_Construct_UScriptStruct_FMyStruct_CRC();
    UPackage* Outer = Z_Construct_UPackage__Script_MyProject();
    static UScriptStruct* ReturnStruct = FindExistingStructIfHotReloadOrDynamic(Outer, TEXT("MyStruct"), sizeof(FMyStruct), Get_Z_Construct_UScriptStruct_FMyStruct_CRC(), false);
#else
    static UScriptStruct* ReturnStruct = nullptr;
#endif
    if (!ReturnStruct)
    {
        UE4CodeGen_Private::ConstructUScriptStruct(ReturnStruct,
                                                   Z_Construct_UScriptStruct_FMyStruct_Statics::ReturnStructParams);
    }
    return ReturnStruct;
}

uint32 Get_Z_Construct_UScriptStruct_FMyStruct_CRC() { return 4118197761U; }

UINTERFACE生成代码

原始代码:

代码语言:txt
复制
#include "CoreMinimal.h"
#include "Interface.h"
#include "MyObject.generated.h"

UINTERFACE()
class UMyInterface : public UInterface
{
    GENERATED_BODY()
};

class IMyInterface
{
    GENERATED_BODY()
public:
    UFUNCTION()
    virtual void InterfaceFunction();
};

展开GENERATED_BODY宏之后的代码:

代码语言:txt
复制
#include "CoreMinimal.h"
#include "Interface.h"

#include "UObject/ObjectMacros.h"
#include "UObject/ScriptMacros.h"

class UMyInterface : public UInterface
{
private:
    static void StaticRegisterNativesUMyInterface();
    friend struct Z_Construct_UClass_UMyInterface_Statics;
public:
private:
    UMyInterface& operator=(UMyInterface&&);
    UMyInterface& operator=(const UMyInterface&);
    static UClass* GetPrivateStaticClass();
public:
    enum { StaticClassFlags=(CLASS_Abstract | CLASS_Interface | CLASS_Intrinsic) };

    typedef UInterface Super;
    typedef UMyInterface ThisClass;
    inline static UClass* StaticClass() { return GetPrivateStaticClass(); }
    inline static const TCHAR* StaticPackage() { return L"/Script/MyProject"; }
    inline static EClassCastFlags StaticClassCastFlags() { return CASTCLASS_None; }

    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);
    }

    inline void* operator new(const size_t InSize, EInternal* InMem) { return (void*)InMem; }
    friend FArchive& operator<<(FArchive& Ar, UMyInterface*& Res) { return Ar << (UObject*&)Res; }
    friend void operator<<(FStructuredArchive::FSlot InSlot, UMyInterface*& Res) { InSlot << (UObject*&)Res; }

    UMyInterface(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer)
    {
    };
private:
    UMyInterface(UMyInterface&&);
    UMyInterface(const UMyInterface&);
public:
    UMyInterface(FVTableHelper& Helper);;
    static UObject* __VTableCtorCaller(FVTableHelper& Helper) { return nullptr; };
    static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())UMyInterface(X); }
private:
    __pragma (warning(pop));
};

class IMyInterface
{
    __pragma (warning(push))
    __pragma (warning(disable:4995))
    __pragma (warning(disable:4996))
public:
    static void execInterfaceFunction(UObject* Context, FFrame& Stack, void* const Z_Param__Result)
    {
        Stack.Code += !!Stack.Code;;
        {
            ;;
            ((ThisClass*)(Context))->InterfaceFunction();
        };
    }

protected:
    virtual ~IMyInterface()
    {
    }

public:
    typedef UMyInterface UClassType;
    typedef IMyInterface ThisClass;

    virtual UObject* _getUObject() const
    {
        {
            if ((!(0 && "Missing required implementation.")))
            {
                FDebug::LogAssertFailedMessage("0 && \"Missing required implementation.\"", "MyObject.h", 141, L"");
                if (!FPlatformMisc::IsDebuggerPresent()) { FPlatformMisc::PromptForRemoteDebugging(false); }
                ((void)(FWindowsPlatformMisc::IsDebuggerPresent() && (__debugbreak(), 1)));;
                FDebug::AssertFailed("0 && \"Missing required implementation.\"", "MyObject.h", 141);;
            }
        };
        return nullptr;
    }

private:
public:
    virtual void InterfaceFunction();
};

.gen.cpp生成代码:

代码语言:txt
复制
#include "UObject/GeneratedCppIncludes.h"
#include "MyProject/MyObject.h"

void EmptyLinkFunctionForGeneratedCodeMyObject()
{
}

// Cross Module References
UClass* Z_Construct_UClass_UMyInterface_NoRegister();
UClass* Z_Construct_UClass_UMyInterface();
UClass* Z_Construct_UClass_UInterface();
UPackage* Z_Construct_UPackage__Script_MyProject();
UFunction* Z_Construct_UFunction_UMyInterface_InterfaceFunction();
// End Cross Module References
void UMyInterface::StaticRegisterNativesUMyInterface()
{
    UClass* Class = UMyInterface::StaticClass();
    static const FNameNativePtrPair Funcs[] = {
        {"InterfaceFunction", &IMyInterface::execInterfaceFunction},
    };
    FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, ARRAY_COUNT(Funcs));
}

struct Z_Construct_UFunction_UMyInterface_InterfaceFunction_Statics
{
#if WITH_METADATA
    static const UE4CodeGen_Private::FMetaDataPairParam Function_MetaDataParams[];
#endif
    static const UE4CodeGen_Private::FFunctionParams FuncParams;
};
#if WITH_METADATA
const UE4CodeGen_Private::FMetaDataPairParam Z_Construct_UFunction_UMyInterface_InterfaceFunction_Statics::
    Function_MetaDataParams[] = {
        {"ModuleRelativePath", "MyObject.h"},
    };
#endif
const UE4CodeGen_Private::FFunctionParams Z_Construct_UFunction_UMyInterface_InterfaceFunction_Statics::FuncParams = {
    (UObject*(*)())Z_Construct_UClass_UMyInterface, "InterfaceFunction", RF_Public | RF_Transient | RF_MarkAsNative,
    nullptr, (EFunctionFlags)0x00020400, 0, nullptr, 0, 0, 0, METADATA_PARAMS(
        Z_Construct_UFunction_UMyInterface_InterfaceFunction_Statics::Function_MetaDataParams,
        ARRAY_COUNT(Z_Construct_UFunction_UMyInterface_InterfaceFunction_Statics::Function_MetaDataParams))
};

UFunction* Z_Construct_UFunction_UMyInterface_InterfaceFunction()
{
    static UFunction* ReturnFunction = nullptr;
    if (!ReturnFunction)
    {
        UE4CodeGen_Private::ConstructUFunction(ReturnFunction,
                                               Z_Construct_UFunction_UMyInterface_InterfaceFunction_Statics::
                                               FuncParams);
    }
    return ReturnFunction;
}

UClass* Z_Construct_UClass_UMyInterface_NoRegister()
{
    return UMyInterface::StaticClass();
}

struct Z_Construct_UClass_UMyInterface_Statics
{
    static UObject* (*const DependentSingletons[])();
    static const FClassFunctionLinkInfo FuncInfo[];
#if WITH_METADATA
    static const UE4CodeGen_Private::FMetaDataPairParam Class_MetaDataParams[];
#endif
    static const FCppClassTypeInfoStatic StaticCppClassTypeInfo;
    static const UE4CodeGen_Private::FClassParams ClassParams;
};

UObject* (*const Z_Construct_UClass_UMyInterface_Statics::DependentSingletons[])() = {
    (UObject* (*)())Z_Construct_UClass_UInterface,
    (UObject* (*)())Z_Construct_UPackage__Script_MyProject,
};
const FClassFunctionLinkInfo Z_Construct_UClass_UMyInterface_Statics::FuncInfo[] = {
    {&Z_Construct_UFunction_UMyInterface_InterfaceFunction, "InterfaceFunction"}, // 156484113
};
#if WITH_METADATA
const UE4CodeGen_Private::FMetaDataPairParam Z_Construct_UClass_UMyInterface_Statics::Class_MetaDataParams[] = {
    {"ModuleRelativePath", "MyObject.h"},
};
#endif
const FCppClassTypeInfoStatic Z_Construct_UClass_UMyInterface_Statics::StaticCppClassTypeInfo = {
    TCppClassTypeTraits<IMyInterface>::IsAbstract,
};
const UE4CodeGen_Private::FClassParams Z_Construct_UClass_UMyInterface_Statics::ClassParams = {
    &UMyInterface::StaticClass,
    DependentSingletons, ARRAY_COUNT(DependentSingletons),
    0x000040A1u,
    FuncInfo, ARRAY_COUNT(FuncInfo),
    nullptr, 0,
    nullptr,
    &StaticCppClassTypeInfo,
    nullptr, 0,
    METADATA_PARAMS(Z_Construct_UClass_UMyInterface_Statics::Class_MetaDataParams,
                    ARRAY_COUNT(Z_Construct_UClass_UMyInterface_Statics::Class_MetaDataParams))
};

UClass* Z_Construct_UClass_UMyInterface()
{
    static UClass* OuterClass = nullptr;
    if (!OuterClass)
    {
        UE4CodeGen_Private::ConstructUClass(OuterClass, Z_Construct_UClass_UMyInterface_Statics::ClassParams);
    }
    return OuterClass;
}

static TClassCompiledInDefer<UMyInterface>
AutoInitializeUMyInterface(L"UMyInterface", sizeof(UMyInterface), 3657494735);

UClass* UMyInterface::GetPrivateStaticClass()
{
    static UClass* PrivateStaticClass = 0;
    if (!PrivateStaticClass)
    {
        GetPrivateStaticClassBody(StaticPackage(),
                                  (TCHAR*)L"UMyInterface" + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0),
                                  PrivateStaticClass, StaticRegisterNativesUMyInterface, sizeof(UMyInterface),
                                  (EClassFlags)UMyInterface::StaticClassFlags, UMyInterface::StaticClassCastFlags(),
                                  UMyInterface::StaticConfigName(),
                                  (UClass::ClassConstructorType)InternalConstructor<UMyInterface>,
                                  (UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<UMyInterface>,
                                  &UMyInterface::AddReferencedObjects, &UMyInterface::Super::StaticClass,
                                  &UMyInterface::WithinClass::StaticClass);
    }
    return PrivateStaticClass;
};
static FCompiledInDefer Z_CompiledInDefer_UClass_UMyInterface(Z_Construct_UClass_UMyInterface,
                                                              &UMyInterface::StaticClass, TEXT("/Script/MyProject"),
                                                              TEXT("UMyInterface"), false, nullptr, nullptr, nullptr);

UMyInterface::UMyInterface(FVTableHelper& Helper) : Super(Helper)
{
};;

UENUM生成代码

原始代码:

代码语言:txt
复制
#include "CoreMinimal.h"
#include "MyObject.generated.h"

UENUM()
enum class EMyEnum
{
    /** Comment 1 */
    EnumValue1 = 1,
    /** Comment 2 */
    EnumValue2 = 2,
};

.gen.cpp生成代码:

代码语言:txt
复制
#include "UObject/GeneratedCppIncludes.h"
#include "MyProject/MyObject.h"

void EmptyLinkFunctionForGeneratedCodeMyObject()
{
}

// Cross Module References
THEMYPROJECT_API UEnum* Z_Construct_UEnum_MyProject_EMyEnum();
UPackage* Z_Construct_UPackage__Script_MyProject();
// End Cross Module References
static UEnum* EMyEnum_StaticEnum()
{
    static UEnum* Singleton = nullptr;
    if (!Singleton)
    {
        Singleton = GetStaticEnum(Z_Construct_UEnum_MyProject_EMyEnum, Z_Construct_UPackage__Script_MyProject(),
                                  TEXT("EMyEnum"));
    }
    return Singleton;
}

static FCompiledInDeferEnum Z_CompiledInDeferEnum_UEnum_EMyEnum(EMyEnum_StaticEnum, TEXT("/Script/MyProject"),
                                                                TEXT("EMyEnum"), false, nullptr, nullptr);
uint32 Get_Z_Construct_UEnum_MyProject_EMyEnum_CRC() { return 2698702339U; }

UEnum* Z_Construct_UEnum_MyProject_EMyEnum()
{
#if WITH_HOT_RELOAD
    UPackage* Outer = Z_Construct_UPackage__Script_MyProject();
    static UEnum* ReturnEnum = FindExistingEnumIfHotReloadOrDynamic(Outer, TEXT("EMyEnum"), 0, Get_Z_Construct_UEnum_MyProject_EMyEnum_CRC(), false);
#else
    static UEnum* ReturnEnum = nullptr;
#endif // WITH_HOT_RELOAD
    if (!ReturnEnum)
    {
        static const UE4CodeGen_Private::FEnumeratorParam Enumerators[] = {
            {"EMyEnum::EnumValue1", (int64)EMyEnum::EnumValue1},
            {"EMyEnum::EnumValue2", (int64)EMyEnum::EnumValue2},
        };
#if WITH_METADATA
        const UE4CodeGen_Private::FMetaDataPairParam Enum_MetaDataParams[] = {
            {"EnumValue1.ToolTip", "Comment 1"},
            {"EnumValue2.ToolTip", "Comment 2"},
            {"ModuleRelativePath", "MyObject.h"},
        };
#endif
        static const UE4CodeGen_Private::FEnumParams EnumParams = {
            (UObject*(*)())Z_Construct_UPackage__Script_MyProject,
            UE4CodeGen_Private::EDynamicType::NotDynamic,
            "EMyEnum",
            RF_Public | RF_Transient | RF_MarkAsNative,
            nullptr,
            (uint8)UEnum::ECppForm::EnumClass,
            "EMyEnum",
            Enumerators,
            ARRAY_COUNT(Enumerators),
            METADATA_PARAMS(Enum_MetaDataParams, ARRAY_COUNT(Enum_MetaDataParams))
        };
        UE4CodeGen_Private::ConstructUEnum(ReturnEnum, EnumParams);
    }
    return ReturnEnum;
}

参考文献

  1. 《Inside UE4》 知乎专栏 https://zhuanlan.zhihu.com/insideue4

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 传统反射实现
  • UE4反射类型定义
    • 反射类型结构总览
    • UHT解析原理
    • 反射代码生成
      • 反射代码注入机制
        • 不同类型生成代码
          • UCLASS
          • UPROPERTY
          • UFUNCTION
          • USTRUCT
          • UINTERFACE
          • UENUM
      • UE4反射系统存储结构及运行机制
        • StaticClass数据收集
          • 用Hash支持快速查找
          • 总结
          • 附录:展开生成代码
            • UCLASS生成代码
              • USTRUCT生成代码
                • UINTERFACE生成代码
                  • UENUM生成代码
                  • 参考文献
                  相关产品与服务
                  腾讯云代码分析
                  腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档