Struct 反射实现分析

前面讲到了 Class/function/property 的反射,UE 还支持结构体的反射,其实从 C++ 的标准语义来说并没有区分“结构”和“类”,关键字 structclass的区别只在于默认的访问权限。

在 UE 里面,支持反射的结构提只能使用struct,并且不能包含任何 UFUNCTION 的函数,命名必须以 F 开头。

1
2
3
4
5
6
7
8
9
10
USTRUCT(BlueprintType)
struct FTestStruct
{
GENERATED_USTRUCT_BODY()

UPROPERTY(EditAnywhere)
int32 ival;
UPROPERTY(EditAnywhere)
UTexture2D* Texture;
};

UE 的标记语法和 Class 的类似,不过 UHT 为 Struct 生成的代码要简单许多,因为没有 UFUNCTION 也没有继承 UObject。

GENERATED_USTRUCT_BODY这个标记 UHT 会展开生成一个真正的 C++ 宏(在 genreated.h 中):

1
2
3
4
5
6
// generated.h
#define HotPatcherExample_Source_HotPatcherExample_TestStruct_h_11_GENERATED_BODY \
friend struct Z_Construct_UScriptStruct_FTestStruct_Statics; \
HOTPATCHEREXAMPLE_API static class UScriptStruct* StaticStruct();

template<> HOTPATCHEREXAMPLE_API UScriptStruct* StaticStruct<struct FTestStruct>();

把 UHT 生成的用于记录当前 struct 反射数据的结构 Z_Construct_UScriptStruct_FTestStruct_Statis 声明为当前 struct 类的友元,可以让它访问到自己的私有成员。而且还声明了当前 Struct 的 static 成员函数 StaticStruct 和全局模板函数StaticStruct<FTestStruct>

对 Struct 生成的反射数;

据的结构与 class 的类似,只有 Property 的反射信息。

gen.cpp中有以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class UScriptStruct* FTestStruct::StaticStruct()
{
static class UScriptStruct* Singleton = NULL;
if (!Singleton)
{
extern HOTPATCHEREXAMPLE_API uint32 Get_Z_Construct_UScriptStruct_FTestStruct_Hash();
Singleton = GetStaticStruct(Z_Construct_UScriptStruct_FTestStruct, Z_Construct_UPackage__Script_HotPatcherExample(), TEXT("TestStruct"), sizeof(FTestStruct), Get_Z_Construct_UScriptStruct_FTestStruct_Hash());
}
return Singleton;
}
template<> HOTPATCHEREXAMPLE_API UScriptStruct* StaticStruct<FTestStruct>()
{
return FTestStruct::StaticStruct();
}
static FCompiledInDeferStruct Z_CompiledInDeferStruct_UScriptStruct_FTestStruct(FTestStruct::StaticStruct, TEXT("/Script/HotPatcherExample"), TEXT("TestStruct"), false, nullptr, nullptr);
static struct FScriptStruct_HotPatcherExample_StaticRegisterNativesFTestStruct
{
FScriptStruct_HotPatcherExample_StaticRegisterNativesFTestStruct()
{
UScriptStruct::DeferCppStructOps(FName(TEXT("TestStruct")),new UScriptStruct::TCppStructOps<FTestStruct>);
}
} ScriptStruct_HotPatcherExample_StaticRegisterNativesFTestStruct;

UScriptStruct* Z_Construct_UScriptStruct_FTestStruct()
{
#if WITH_HOT_RELOAD
extern uint32 Get_Z_Construct_UScriptStruct_FTestStruct_Hash();
UPackage* Outer = Z_Construct_UPackage__Script_HotPatcherExample();
static UScriptStruct* ReturnStruct = FindExistingStructIfHotReloadOrDynamic(Outer, TEXT("TestStruct"), sizeof(FTestStruct), Get_Z_Construct_UScriptStruct_FTestStruct_Hash(), false);
#else
static UScriptStruct* ReturnStruct = nullptr;
#endif
if (!ReturnStruct)
{
UE4CodeGen_Private::ConstructUScriptStruct(ReturnStruct, Z_Construct_UScriptStruct_FTestStruct_Statics::ReturnStructParams);
}
return ReturnStruct;
}
uint32 Get_Z_Construct_UScriptStruct_FTestStruct_Hash() { return 4266809061U; }

上面的代码包含了从 UHT 生成的反射信息中构造出 UStructSctruct 以及延迟注册的方法,和 Class 的方式类似。

还包含生成的结构 Z_Construct_UScriptStruct_STRUCTNAME_Statics 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
struct Z_Construct_UScriptStruct_FTestStruct_Statics
{
#if WITH_METADATA
static const UE4CodeGen_Private::FMetaDataPairParam Struct_MetaDataParams[];
#endif
static void* NewStructOps();
#if WITH_METADATA
static const UE4CodeGen_Private::FMetaDataPairParam NewProp_Texture_MetaData[];
#endif
static const UE4CodeGen_Private::FObjectPropertyParams NewProp_Texture;
#if WITH_METADATA
static const UE4CodeGen_Private::FMetaDataPairParam NewProp_ival_MetaData[];
#endif
static const UE4CodeGen_Private::FIntPropertyParams NewProp_ival;
static const UE4CodeGen_Private::FPropertyParamsBase* const PropPointers[];
static const UE4CodeGen_Private::FStructParams ReturnStructParams;
};

#if WITH_METADATA
const UE4CodeGen_Private::FMetaDataPairParam Z_Construct_UScriptStruct_FTestStruct_Statics::Struct_MetaDataParams[] = {
{ "BlueprintType", "true" },
{ "ModuleRelativePath", "TestStruct.h" },
};
#endif
void* Z_Construct_UScriptStruct_FTestStruct_Statics::NewStructOps()
{
return (UScriptStruct::ICppStructOps*)new UScriptStruct::TCppStructOps<FTestStruct>();
}
#if WITH_METADATA
const UE4CodeGen_Private::FMetaDataPairParam Z_Construct_UScriptStruct_FTestStruct_Statics::NewProp_Texture_MetaData[] = {
{ "Category", "TestStruct" },
{ "ModuleRelativePath", "TestStruct.h" },
};
#endif

const UE4CodeGen_Private::FObjectPropertyParams Z_Construct_UScriptStruct_FTestStruct_Statics::NewProp_Texture = {
"Texture",
nullptr,
(EPropertyFlags)0x0010000000000001,
UE4CodeGen_Private::EPropertyGenFlags::Object,
RF_Public|RF_Transient|RF_MarkAsNative,
1,
STRUCT_OFFSET(FTestStruct, Texture),
Z_Construct_UClass_UTexture2D_NoRegister,
METADATA_PARAMS(Z_Construct_UScriptStruct_FTestStruct_Statics::NewProp_Texture_MetaData,
UE_ARRAY_COUNT(Z_Construct_UScriptStruct_FTestStruct_Statics::NewProp_Texture_MetaData))
};
#if WITH_METADATA
const UE4CodeGen_Private::FMetaDataPairParam Z_Construct_UScriptStruct_FTestStruct_Statics::NewProp_ival_MetaData[] = {
{ "Category", "TestStruct" },
{ "ModuleRelativePath", "TestStruct.h" },
};
#endif
const UE4CodeGen_Private::FIntPropertyParams Z_Construct_UScriptStruct_FTestStruct_Statics::NewProp_ival = {
"ival",
nullptr,
(EPropertyFlags)0x0010000000000001,
UE4CodeGen_Private::EPropertyGenFlags::Int,
RF_Public|RF_Transient|RF_MarkAsNative,
1,
STRUCT_OFFSET(FTestStruct, ival),
METADATA_PARAMS(Z_Construct_UScriptStruct_FTestStruct_Statics::NewProp_ival_MetaData,
UE_ARRAY_COUNT(Z_Construct_UScriptStruct_FTestStruct_Statics::NewProp_ival_MetaData))
};

const UE4CodeGen_Private::FPropertyParamsBase* const Z_Construct_UScriptStruct_FTestStruct_Statics::PropPointers[] = {
(const UE4CodeGen_Private::FPropertyParamsBase*)&Z_Construct_UScriptStruct_FTestStruct_Statics::NewProp_Texture,
(const UE4CodeGen_Private::FPropertyParamsBase*)&Z_Construct_UScriptStruct_FTestStruct_Statics::NewProp_ival,
};
const UE4CodeGen_Private::FStructParams Z_Construct_UScriptStruct_FTestStruct_Statics::ReturnStructParams = {
(UObject* (*)())Z_Construct_UPackage__Script_HotPatcherExample,
nullptr,
&NewStructOps,
"TestStruct",
sizeof(FTestStruct),
alignof(FTestStruct),
Z_Construct_UScriptStruct_FTestStruct_Statics::PropPointers,
UE_ARRAY_COUNT(Z_Construct_UScriptStruct_FTestStruct_Statics::PropPointers),
RF_Public|RF_Transient|RF_MarkAsNative,
EStructFlags(0x00000001),
METADATA_PARAMS(Z_Construct_UScriptStruct_FTestStruct_Statics::Struct_MetaDataParams, UE_ARRAY_COUNT(Z_Construct_UScriptStruct_FTestStruct_Statics::Struct_MetaDataParams))
};

可以看到 Struct 的每个属性也是通过 FPropertyParamsBase 来存储的,与 Class 一致。

区别在于,Struct 使用 UE4CodeGen_Private::FStructParams 来存储当前结构的反射信息,其声明如下(CoreUObject/Public/UObject/UObjectGlobals.h):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// CoreUObject/Public/UObject/UObjectGlobals.h
struct FStructParams
{
UObject* (*OuterFunc)();
UScriptStruct* (*SuperFunc)();
void* (*StructOpsFunc)(); // really returns UScriptStruct::ICppStructOps*
const char* NameUTF8;
SIZE_T SizeOf;
SIZE_T AlignOf;
const FPropertyParamsBase* const* PropertyArray;
int32 NumProperties;
EObjectFlags ObjectFlags;
uint32 StructFlags; // EStructFlags
#if WITH_METADATA
const FMetaDataPairParam* MetaDataArray;
int32 NumMetaData;
#endif
};

这个结构中比较特殊的一点是 StructOpsFunc 是一个函数指针,用来管理 C++ 结构的构造和析构,使用的也是 placement-new 的方式,TCppStructOps<>模板定义在CoreUObject/Public/UObject/Class.h