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