UE4 反射系统
UE4 反射系统
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力,即在运行过程中检查自己的C++类,函数,成员变量,结构体等等。
在UE4里面,通过如下的宏定义,来实现发射:
#define UPROPERTY(...)
#define UFUNCTION(...)
#define USTRUCT(...)
#define UMETA(...)
#define UPARAM(...)
#define UENUM(...)
#define UDELEGATE(...)
#define UCLASS(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_PROLOG)
#define UINTERFACE(...) UCLASS()
这些宏分别用在属性,函数,结构体/类型定义等,然后有这些宏定义的属性,函数,类型就能够被蓝图调用
反射实现机制和基本原理
在了解反射系统之前,我们必须要知道两个UE4特有的文件类型
- .generate.h
- .gen.cpp
UE4 在编译之前,会给每一个带有 UCLASS 的文件生成一个对应的 “.generate.h”,并且要求代码 include 这个头文件(并且是在其他 include 之后),否则无法正常编译。这两个文件都是通过 UBT 和 UHT 生成的。
UBT 和 UHT
UnrealHeaderTool (UHT,C++):UE4的C代码解析生成工具,我们在代码里写的那些宏 UCLASS 等和 #include “*.generated.h” 都为 UHT 提供了信息来生成相应的 C 反射代码
UnrealBuildTool(UBT,C#):UE4的自定义工具,来编译 UE4 的逐个模块并处理依赖等。我们编写的 Target.cs,Build.cs 都是为这个工具服务的。
UE4 中代码编译分两个阶段进行:
- UHT 被调用。它将解析 C++ 头中引擎相关类元数据,并生成自定义代码,以实现诸多 UObject 相关的功能。
- 普通 C++ 编译器被调用,以便对结果进行编译。
UBT
UBT主要责任是UE4的各个模块的编译并处理各模块之间的依赖关系的。build.cs和Target.cs都是为这个工具来服务的。
// Test.Target.cs
using UnrealBuildTool;
using System.Collections.Generic;
public class TestTarget : TargetRules
{
public TestTarget( TargetInfo Target) : base(Target)
{
Type = TargetType.Game;
DefaultBuildSettings = BuildSettingsVersion.V2;
ExtraModuleNames.AddRange( new string[] { "Test" } );
}
}
UBT 支持多种 TargetType
- Game - 需要烘焙数据来运行的独立游戏;
- Client - 与Game相同,但包不含任何服务器代码,适用于联网游戏。
- Server - 与Game相同,但不包含客户端代码,适用于联网游戏种的独立服务器。
- Editor - 扩展编辑器的一种targetType。
- Program - 基于虚幻引擎打造的独立工具程序。
Modules
UE4 引擎是由大量的模块集合实现的,模块是通过 C# 源文件声明的,扩展名为 .build.cs,存储在项目的 Source 目录下,每一个 .build.cs 都声明一个类,继承 ModuleRules 基类。
using UnrealBuildTool;
using System.Collections.Generic;
public class MyModule :ModuleRules
{
public MyModule(ReadOnlyTargetRules Target) : base(Target)
{
// Settings go here
}
}
UHT 预处理
我们编写过程中用到的 UCLASS 、UFUNCTION 这些宏就是参与这个阶段,当 UHT 处理代码时,遇到这些宏标记,就会对代码进行对应的预处理,在正常的 C++ 编译预处理过程中,这些宏会被展开,只不过展开内容是空。
UHT 生成的代码分别在 generated.h 和 gen.cpp 中,generated.h 中的代码大多是一些宏定义,用在所声明的类中,编译器预处理时可以增加通用成员,gen.cpp 则是 UHT 给予反射标记生成的用来描述类反射信息的具体代码。
UE4 与反射相关的 UHT 宏标记大多定义在以下几个头文件中:
- Runtime/CoreUObject/Public/Object/ObjectMacros.h (UHT 标记 UPROPERY等)
- Runtime/CoreUObject/Public/Object/ScriptMacros.h(大多是 P_* 的宏,可以利用反射从 Stack 中获取数据)
- Runtime/CoreUObject/Public/UObject/Class.h (反射基类的定义 UField/UEnum/UStruct/UClass 等)
下面是我们通过 UE4 编辑器生成的一个 C++ 类
// MyActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
UCLASS()
class TEST_API AMyActor : public AActor
{
GENERATED_BODY() // Line:12
public:
AMyActor();
UFUNCTION()
int GetValue() const;
UFUNCTION()
void SetValue(int InValue);
void SetValueNoneU(int IntValue);
private:
UPROPERTY()
int Value;
}
/// MyActor.cpp
#include "MyActor.h"
AMyActor::AMyActor()
{
PrimaryActorTick.bCanEverTick = true;
}
int AMyActor::GetValue() const
{
return Value;
}
void AMyActor::SetValue(int InValue)
{
Value = InValue;
}
void AMyActor::SetValueNoneU(int InValue)
{
Value = InValue;
}
void AMyActor::BeginPlay()
{
Super::BeginPlay();
}
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
当我们在编译时,UBT 会驱动 UHT 为这个类生成 MyActor.generated.h 和 MyActor.gen.cpp 文件。
*.generated.h 与 *.gen.cpp 文件存放与下列路径 (相对于项目根目录):
Intermediate\Build\Win64\UE4Editor\Inc\{PROJECT_NAME}
GENERATED_BODY
在正式分析这两个文件前,先介绍下 GENERATED_BODY 与 GENERATED_UCLASS_BODY 宏。
// This pair of macros is used to help implement GENERATED_BODY() and GENERATED_USTRUCT_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)
// Include a redundant semicolon at the end of the generated code block, so that intellisense parsers can start parsing
// a new declaration if the line number/generated code is out of date.
#define GENERATED_BODY_LEGACY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY_LEGACY);
#define GENERATED_BODY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY);
#define GENERATED_USTRUCT_BODY(...) GENERATED_BODY()
#define GENERATED_UCLASS_BODY(...) GENERATED_BODY_LEGACY()
#define GENERATED_UINTERFACE_BODY(...) GENERATED_BODY_LEGACY()
#define GENERATED_IINTERFACE_BODY(...) GENERATED_BODY_LEGACY()
单纯的从宏展开角度看,GENERATED_BODY 与 GENERATED_UCLASS_BODY 区别就是:
// GENERATED_BODY最终生成了这样的一串字符:
{CURRENT_FILE_ID}_{__LINE__}_GENERATED_BODY
// GENERATED_UCLASS_BODY最终生成的是这样的一串字符串:
{CURRENT_FILE_ID}_{__LINE__}_GENERATED_BODY_LEGACY
注意:这里用 {} 括着的是其他的宏组成的,这里只是列出来两个宏的不同形式。
CURRENT_FILE_ID 为项目所在的文件夹的名字_源文件相对路径_h
__LINE__ 为代码所在行号,也就是上面代码 GENERATED_BODY 所在的第 12 行。
则这两个宏实际拼接后的字符串如下:
// e.g
// Test\Source\Test\MyActor.h
// GENERATED_BODY
Test_Source_Test_MyActor_h_12_GENERATED_BODY
// GENERATED_BODY_LEGACY
Test_Source_Test_MyActor_h_12_GENERATED_BODY_LEGACY
然后打开 MyActor.generated.h 文件,仔细看其中定义的宏代码:
// Copyright Epic Games, Inc. All Rights Reserved.
/*===========================================================================
Generated code exported from UnrealHeaderTool.
DO NOT modify this manually! Edit the corresponding .h files instead!
===========================================================================*/
#include "UObject/ObjectMacros.h"
#include "UObject/ScriptMacros.h"
PRAGMA_DISABLE_DEPRECATION_WARNINGS
#ifdef TEST_MyActor_generated_h
#error "MyActor.generated.h already included, missing '#pragma once' in MyActor.h"
#endif
#define TEST_MyActor_generated_h
#define Test_Source_Test_MyActor_h_12_SPARSE_DATA
#define Test_Source_Test_MyActor_h_12_RPC_WRAPPERS \
\
DECLARE_FUNCTION(execSetValue); \
DECLARE_FUNCTION(execGetValue);
#define Test_Source_Test_MyActor_h_12_RPC_WRAPPERS_NO_PURE_DECLS \
\
DECLARE_FUNCTION(execSetValue); \
DECLARE_FUNCTION(execGetValue);
#define Test_Source_Test_MyActor_h_12_INCLASS_NO_PURE_DECLS \
private: \
static void StaticRegisterNativesAMyActor(); \
friend struct Z_Construct_UClass_AMyActor_Statics; \
public: \
DECLARE_CLASS(AMyActor, AActor, COMPILED_IN_FLAGS(0 | CLASS_Config), CASTCLASS_None, TEXT("/Script/Test"), NO_API) \
DECLARE_SERIALIZER(AMyActor)
#define Test_Source_Test_MyActor_h_12_INCLASS \
private: \
static void StaticRegisterNativesAMyActor(); \
friend struct Z_Construct_UClass_AMyActor_Statics; \
public: \
DECLARE_CLASS(AMyActor, AActor, COMPILED_IN_FLAGS(0 | CLASS_Config), CASTCLASS_None, TEXT("/Script/Test"), NO_API) \
DECLARE_SERIALIZER(AMyActor)
#define Test_Source_Test_MyActor_h_12_STANDARD_CONSTRUCTORS \
/** Standard constructor, called after all reflected properties have been initialized */ \
NO_API AMyActor(const FObjectInitializer& ObjectInitializer); \
DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(AMyActor) \
DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, AMyActor); \
DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(AMyActor); \
private: \
/** Private move- and copy-constructors, should never be used */ \
NO_API AMyActor(AMyActor&&); \
NO_API AMyActor(const AMyActor&); \
public:
#define Test_Source_Test_MyActor_h_12_ENHANCED_CONSTRUCTORS \
private: \
/** Private move- and copy-constructors, should never be used */ \
NO_API AMyActor(AMyActor&&); \
NO_API AMyActor(const AMyActor&); \
public: \
DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, AMyActor); \
DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(AMyActor); \
DEFINE_DEFAULT_CONSTRUCTOR_CALL(AMyActor)
#define Test_Source_Test_MyActor_h_12_PRIVATE_PROPERTY_OFFSET \
FORCEINLINE static uint32 __PPO__Value() { return STRUCT_OFFSET(AMyActor, Value); }
#define Test_Source_Test_MyActor_h_9_PROLOG
#define Test_Source_Test_MyActor_h_12_GENERATED_BODY_LEGACY \
PRAGMA_DISABLE_DEPRECATION_WARNINGS \
public: \
Test_Source_Test_MyActor_h_12_PRIVATE_PROPERTY_OFFSET \
Test_Source_Test_MyActor_h_12_SPARSE_DATA \
Test_Source_Test_MyActor_h_12_RPC_WRAPPERS \
Test_Source_Test_MyActor_h_12_INCLASS \
Test_Source_Test_MyActor_h_12_STANDARD_CONSTRUCTORS \
public: \
PRAGMA_ENABLE_DEPRECATION_WARNINGS
#define Test_Source_Test_MyActor_h_12_GENERATED_BODY \
PRAGMA_DISABLE_DEPRECATION_WARNINGS \
public: \
Test_Source_Test_MyActor_h_12_PRIVATE_PROPERTY_OFFSET \
Test_Source_Test_MyActor_h_12_SPARSE_DATA \
Test_Source_Test_MyActor_h_12_RPC_WRAPPERS_NO_PURE_DECLS \
Test_Source_Test_MyActor_h_12_INCLASS_NO_PURE_DECLS \
Test_Source_Test_MyActor_h_12_ENHANCED_CONSTRUCTORS \
private: \
PRAGMA_ENABLE_DEPRECATION_WARNINGS
template<> TEST_API UClass* StaticClass<class AMyActor>();
#undef CURRENT_FILE_ID
#define CURRENT_FILE_ID Test_Source_Test_MyActor_h
PRAGMA_ENABLE_DEPRECATION_WARNINGS
里面包含了我们刚刚写的那些宏定义:
#define CURRENT_FILE_ID Test_Source_Test_MyActor_h
#define Test_Source_Test_MyActor_h_12_GENERATED_BODY_LEGACY
#define Test_Source_Test_MyActor_h_12_GENERATED_BODY
因此我们 MyActor.h 用到的 GENERATED_BODY 的代码对应如下宏定义:
其实 GENERATED_BODY 与 GENERATED_UCLASS_BODY 的区别在于:Test_Source_Test_MyActor_h_12_ENHANCED_CONSTRUCTORS 跟 Test_Source_Test_MyActor_h_12_STANDARD_CONSTRUCTORS 这两个宏,GENERATED_UCLASS_BODY 多了一个 AMyActor(const FObjectInitializer& ObjectInitializer); 的声明。
#define Test_Source_Test_MyActor_h_12_GENERATED_BODY \
PRAGMA_DISABLE_DEPRECATION_WARNINGS \
public: \
Test_Source_Test_MyActor_h_12_PRIVATE_PROPERTY_OFFSET \
Test_Source_Test_MyActor_h_12_SPARSE_DATA \
Test_Source_Test_MyActor_h_12_RPC_WRAPPERS_NO_PURE_DECLS \
Test_Source_Test_MyActor_h_12_INCLASS_NO_PURE_DECLS \
Test_Source_Test_MyActor_h_12_ENHANCED_CONSTRUCTORS \
private: \
PRAGMA_ENABLE_DEPRECATION_WARNINGS
展开之后代码:
// MyActor.generated.h
class AMyActor : public AActor
{
/// begin GENERATED_BODY()
public:
// mark 1:
DECLARE_FUNCTION(execSetValue);
DECLARE_FUNCTION(execGetValue);
private:
static void StaticRegisterNativesAMyActor();
friend struct Z_Construct_UClass_AMyActor_Statics;
public:
// mark 2:
DECLARE_CLASS(AMyActor, AActor, COMPILED_IN_FLAGS(0 | CLASS_Config), CASTCLASS_None, TEXT("/Script/Test"), NO_API)
// mark 3:
DECLARE_SERIALIZER(AMyActor)
private:
/** Private move- and copy-constructors, should never be used */
NO_API AMyActor(AMyActor&&);
NO_API AMyActor(const AMyActor&);
public:
DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, AMyActor);
DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(AMyActor);
DEFINE_DEFAULT_CONSTRUCTOR_CALL(AMyActor)
/// end GENERATED_BODY()
public:
AMyActor();
UFUNCTION()
int GetValue() const;
UFUNCTION()
void SetValue(int InValue);
void SetValueNoneU(int IntValue);
private:
UPROPERTY()
int Value;
};
// MyActor.gen.cpp
#include "Test/MyActor.h"
DEFINE_FUNCTION(AMyActor::execSetValue)
{
P_GET_PROPERTY(FIntProperty,Z_Param_InValue);
P_FINISH;
P_NATIVE_BEGIN;
P_THIS->SetValue(Z_Param_InValue);
P_NATIVE_END;
}
DEFINE_FUNCTION(AMyActor::execGetValue)
{
P_FINISH;
P_NATIVE_BEGIN;
*(int32*)Z_Param__Result=P_THIS->GetValue();
P_NATIVE_END;
}
void AMyActor::StaticRegisterNativesAMyActor()
{
UClass* Class = AMyActor::StaticClass();
static const FNameNativePtrPair Funcs[] = {
{ "GetValue", &AMyActor::execGetValue },
{ "SetValue", &AMyActor::execSetValue },
};
FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs));
}
IMPLEMENT_CLASS(AMyActor, 1716205458);
template<> TEST_API UClass* StaticClass<AMyActor>()
{
return AMyActor::StaticClass();
}
其中
- DECLARE_FUNCTION 为使用 UFUNCIONT 标记的函数创建中间函数;
- DECLARE_CLASS :声明定义当前类的几个关键信息:Super 和 ThisClass 等 typedef 在此处被定义,以及 StaticClass/StaticPackage/StaticClassCastFlags 和重载的 new 也被定义;
- DECLARE_SERIALIZER:重载 << 使可以被 FArchive 序列化;
- DECLARE_VTABLE_PTR_HELPER_CTOR:声明一个接收 FVTableHelper& 参数的构造函数;
- DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL:定义一个名为__DefaultConstructor 的静态函数,其中是调用 placement-new 创建类对象(用于统一的内存分配),引擎中唯一调用的位置是在 Class.h 的模板函数 InternalConstructor;
placement-new : 就是在用户指定的内存位置上构建新的对象,这个构建过程不需要额外分配内存,只需要调用对象的构造函数即可。
char* buff = new char[ sizeof(Foo) * N ];
memset( buff, 0, sizeof(Foo)*N );
/// placement-new
Foo* pfoo = new (buff)Foo;
DECLARE_CLASS
private:
MyActor& operator=(MyActor&&);
MyActor& operator=(const MyActor&);
TRequiredAPI static UClass* GetPrivateStaticClass();
public:
/** Bitwise union of #EClassFlags pertaining to this class.*/
enum {StaticClassFlags=TStaticFlags};
/** Typedef for the base class ({{ typedef-type }}) */
typedef AActor Super;
/** Typedef for {{ typedef-type }}. */
typedef MyActor ThisClass;
/** Returns a UClass object representing this class at runtime */
inline static UClass* StaticClass()
{
return GetPrivateStaticClass();
}
/** Returns the package this class belongs in */
inline static const TCHAR* StaticPackage()
{
return TEXT("/Script/Test");
}
/** Returns the static cast flags for this class */
inline static EClassCastFlags StaticClassCastFlags()
{
return CASTCLASS_None;
}
/** 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;
}
这里包含了一系列函数、typedef 以及序列化、new 等。
IMPLEMENT_CLASS
// IMPLEMENT_CLASS(AMyActor, 1716205458);
// Register a class at startup time.
#define IMPLEMENT_CLASS(TClass, TClassCrc) \
static TClassCompiledInDefer<TClass> AutoInitialize##TClass(TEXT(#TClass), sizeof(TClass), TClassCrc); \
UClass* TClass::GetPrivateStaticClass() \
{ \
static UClass* PrivateStaticClass = NULL; \
if (!PrivateStaticClass) \
{ \
/* this could be handled with templates, but we want it external to avoid code bloat */ \
GetPrivateStaticClassBody( \
StaticPackage(), \
(TCHAR*)TEXT(#TClass) + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0), \
PrivateStaticClass, \
StaticRegisterNatives##TClass, \
sizeof(TClass), \
alignof(TClass), \
(EClassFlags)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 (定义在 Class.cpp),作用是从当前类的信息构造出一个 UClass 单例对象,调用 GetPrivateStaticClassBody 创建 UClass 对象,并保存为 PrivateStaticClass 变量,在 DECLARE_CLASS 展开的 StaticClass 函数中,返回的就是这个对象。
UFUCTION
UHT 扫描代码中所有标记了 UFUCTION 的函数,会生成对应名为 execFUNC_NAME 的中间函数定义(也称作 thunk 函数)。它统一了所有 UFUNCTION 函数调用规则(this/调用参数/函数返回值),并且包裹了真正要执行的函数。
所有 UNFUNCTION 函数注册过程如下:
- IMPLEMENT_CLASS :调用 RegisterNativeFunc 注册 UFUNCTION 函数列表,存储到 Class::NativeFunctionLookupTable
- Z_Construct_UClass_AMyActor :创建 UClass 对象,最终调用 Z_Construct_UFunction_AMyActor_GetValue 创建 UFunction 对象。
- NewFunction->Bind() : 生成 UFunction 对象后,要进行 bind,bind 到 Class::NativeFunctionLookupTable 对应名字的函数指针,即 &AMyActor::execGetValue
- AddFunctionToFunctionMap :绑定好的 UFunction 对象会保存在 FuncMap
// step 1:
void AMyActor::StaticRegisterNativesAMyActor()
{
UClass* Class = AMyActor::StaticClass();
static const FNameNativePtrPair Funcs[] = {
{ "GetValue", &AMyActor::execGetValue },
{ "SetValue", &AMyActor::execSetValue },
};
FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs));
}
void UClass::AddNativeFunction(const WIDECHAR* InName, FNativeFuncPtr InPointer)
{
new(NativeFunctionLookupTable)FNativeFunctionLookup(InFName, InPointer);
}
// step 2: 直接看到 Z_Construct_UClass_AMyActor
const FClassFunctionLinkInfo Z_Construct_UClass_AMyActor_Statics::FuncInfo[] = {
{ &Z_Construct_UFunction_AMyActor_GetValue, "GetValue" }, // 4052474662
{ &Z_Construct_UFunction_AMyActor_SetValue, "SetValue" }, // 2571773712
};
const UE4CodeGen_Private::FClassParams Z_Construct_UClass_AMyActor_Statics::ClassParams = {
&AMyActor::StaticClass,
"Engine",
&StaticCppClassTypeInfo,
DependentSingletons,
FuncInfo, // FunctionLinkArray
Z_Construct_UClass_AMyActor_Statics::PropPointers,
nullptr,
UE_ARRAY_COUNT(DependentSingletons),
UE_ARRAY_COUNT(FuncInfo),
UE_ARRAY_COUNT(Z_Construct_UClass_AMyActor_Statics::PropPointers),
0,
0x009000A4u,
METADATA_PARAMS(Z_Construct_UClass_AMyActor_Statics::Class_MetaDataParams,
UE_ARRAY_COUNT(Z_Construct_UClass_AMyActor_Statics::Class_MetaDataParams))
};
// 注意 ClassParams
UClass* Z_Construct_UClass_AMyActor()
{
static UClass* OuterClass = nullptr;
if (!OuterClass)
{
UE4CodeGen_Private::ConstructUClass(OuterClass, Z_Construct_UClass_AMyActor_Statics::ClassParams);
}
return OuterClass;
}
/// UObjectGlobals.cpp
void ConstructUClass(UClass*& OutClass, const FClassParams& Params)
{
/// Params.FunctionLinkArray -> ClassParams.FuncInfo
NewClass->CreateLinkAndAddChildFunctionsToMap(Params.FunctionLinkArray, Params.NumFunctions);
}
/// Class.cpp
/// Functions->CreateFuncPtr -> &Z_Construct_UFunction_AMyActor_GetValue
void UClass::CreateLinkAndAddChildFunctionsToMap(const FClassFunctionLinkInfo* Functions, uint32 NumFunctions)
{
for (; NumFunctions; --NumFunctions, ++Functions)
{
const char* FuncNameUTF8 = Functions->FuncNameUTF8;
UFunction* Func = Functions->CreateFuncPtr();
Func->Next = Children;
Children = Func;
AddFunctionToFunctionMap(Func, FName(UTF8_TO_TCHAR(FuncNameUTF8)));
}
}
// Functions->CreateFuncPtr()
// MyActor.gen.cpp
UFunction* Z_Construct_UFunction_AMyActor_GetValue()
{
static UFunction* ReturnFunction = nullptr;
if (!ReturnFunction)
{
UE4CodeGen_Private::ConstructUFunction(ReturnFunction,
Z_Construct_UFunction_AMyActor_GetValue_Statics::FuncParams);
}
return ReturnFunction;
}
void ConstructUFunction(UFunction*& OutFunction, const FFunctionParams& Params)
{
UFunction* NewFunction;
NewFunction->Bind();
}
// step 3:
// Class.cpp
void UFunction::Bind()
{
UClass* OwnerClass = GetOwnerClass();
FName Name = GetFName();
FNativeFunctionLookup* Found = OwnerClass->NativeFunctionLookupTable.FindByPredicate([=](
const FNativeFunctionLookup& NativeFunctionLookup)
{ return Name == NativeFunctionLookup.Name; });
if (Found)
{
Func = Found->Pointer;
}
}
// step 4:
// Class.h
void AddFunctionToFunctionMap(UFunction* Function, FName FuncName)
{
FuncMap.Add(FuncName, Function);
}
// Find
UFunction* UClass::FindFunctionByName(FName InName, EIncludeSuperFlag::Type IncludeSuper) const
{
UFunction* Result = FuncMap.FindRef(InName);
if (Result == nullptr && IncludeSuper == EIncludeSuperFlag::IncludeSuper)
{
/// 在基类上查找函数
}
return Result;
}
经过以上操作后,便可以通过反射来调用 UFUNCTION 函数了
- 首先通过 FindFunctionChecked 获取到函数
- 在通过调用 ProcessEvent 函数执行
{
UFunction* funcSetValue = pMyActor->FindFunctionChecked("GetValue");
if(funcSetValue)
{
// struct define in scope
struct funcSetValueParams{int32 InValue;}InsParam;
InsParam.InValue = 123;
// call SetHp
ProcessEvent(funcSetValue,(void*)(&InsParam));
}
}
下面继续说 DEFINE_FUNCTION
DEFINE_FUNCTION(AMyActor::execSetValue)
{
P_GET_PROPERTY(FIntProperty,Z_Param_InValue);
P_FINISH;
P_NATIVE_BEGIN;
P_THIS->SetValue(Z_Param_InValue);
P_NATIVE_END;
}
void AMyActor::execSetValue( UObject* Context, FFrame& Stack, RESULT_DECL)
{
PropertyType::FIntProperty Z_Param_InValue = PropertyType::GetDefaultPropertyValue();
Stack.StepCompiledIn<PropertyType>(&Z_Param_InValue);
Stack.Code += !!Stack.Code;
{ SCOPED_SCRIPT_NATIVE_TIMER(ScopedNativeCallTimer);
((ThisClass*)(Context))->SetValue(Z_Param_InValue);
}
// Runtime/CoreUObject/Public/Script.h
//
// Blueprint VM intrinsic return value declaration.
//
#define RESULT_PARAM Z_Param__Result
#define RESULT_DECL void*const RESULT_PARAM
// RESULT_DECL
void*const Z_Param__Result
UPROPERTY
在类内对属性加了 UPROPERTY 的标记,不会在 generated.h 中产生额外的代码,但是它会把它的反射信息代码生成到在 gen.cpp 中。
同样是在
const UE4CodeGen_Private::FUnsizedIntPropertyParams Z_Construct_UClass_AMyActor_Statics::NewProp_Value = { "Value", nullptr,
(EPropertyFlags)0x0040000000000000, UE4CodeGen_Private::EPropertyGenFlags::Int, RF_Public|RF_Transient|RF_MarkAsNative,
1, STRUCT_OFFSET(AMyActor, Value), METADATA_PARAMS(Z_Construct_UClass_AMyActor_Statics::NewProp_Value_MetaData,
UE_ARRAY_COUNT(Z_Construct_UClass_AMyActor_Statics::NewProp_Value_MetaData)) };
const UE4CodeGen_Private::FPropertyParamsBase* const Z_Construct_UClass_AMyActor_Statics::PropPointers[] = {
(const UE4CodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_AMyActor_Statics::NewProp_Value,
};
const FCppClassTypeInfoStatic Z_Construct_UClass_AMyActor_Statics::StaticCppClassTypeInfo = {
TCppClassTypeTraits<AMyActor>::IsAbstract,
};
const UE4CodeGen_Private::FClassParams Z_Construct_UClass_AMyActor_Statics::ClassParams = {
&AMyActor::StaticClass,
"Engine",
&StaticCppClassTypeInfo,
DependentSingletons,
FuncInfo,
Z_Construct_UClass_AMyActor_Statics::PropPointers, /// PropertyArray
nullptr,
UE_ARRAY_COUNT(DependentSingletons),
UE_ARRAY_COUNT(FuncInfo),
UE_ARRAY_COUNT(Z_Construct_UClass_AMyActor_Statics::PropPointers),
0,
0x009000A4u,
METADATA_PARAMS(Z_Construct_UClass_AMyActor_Statics::Class_MetaDataParams,
UE_ARRAY_COUNT(Z_Construct_UClass_AMyActor_Statics::Class_MetaDataParams))
};
UClass* Z_Construct_UClass_AMyActor()
{
static UClass* OuterClass = nullptr;
if (!OuterClass)
{
UE4CodeGen_Private::ConstructUClass(OuterClass, Z_Construct_UClass_AMyActor_Statics::ClassParams);
}
return OuterClass;
}
// UObjectGlobals.cpp
void ConstructUClass(UClass*& OutClass, const FClassParams& Params)
{
/// Params.FunctionLinkArray -> ClassParams.FuncInfo
ConstructFProperties(NewClass, Params.PropertyArray, Params.NumProperties);
}
namespace UE4CodeGen_Private
{
void ConstructFProperty(FFieldVariant Outer, const FPropertyParamsBase* const*& PropertyArray, int32& NumProperties)
{
const FPropertyParamsBase* PropBase = *--PropertyArray;
uint32 ReadMore = 0;
FProperty* NewProp = nullptr;
switch (PropBase->Flags & PropertyTypeMask)
{
default:
{
// Unsupported property type
check(false);
}
case EPropertyGenFlags::Byte:
break;
case EPropertyGenFlags::Int:
{
const FIntPropertyParams* Prop = (const FIntPropertyParams*)PropBase;
NewProp = new FIntProperty(Outer, UTF8_TO_TCHAR(Prop->NameUTF8), Prop->ObjectFlags,
Prop->Offset, Prop->PropertyFlags);
#if WITH_METADATA
MetaDataArray = Prop->MetaDataArray;
NumMetaData = Prop->NumMetaData;
#endif
}
break;
}
}
}