引言

上文講到了UE的類型系統(tǒng)結(jié)構(gòu),以及UHT分析源碼的一些宏標(biāo)記設(shè)定。在已經(jīng)進(jìn)行了類型系統(tǒng)整體的設(shè)計(jì)之后,本文將開始討論接下來的步驟。暫時(shí)不討論UHT的細(xì)節(jié),假設(shè)UHT已經(jīng)分析得到了足夠的類型元數(shù)據(jù)信息,下一步就是利用這個(gè)信息在程序內(nèi)存中構(gòu)建起前文的類型系統(tǒng)結(jié)構(gòu),這個(gè)過程我們稱之為注冊(cè)。同一般程序的構(gòu)建流程需要經(jīng)過預(yù)處理、編譯、匯編、鏈接一樣,UE為了在內(nèi)存中模擬構(gòu)建的過程,在概念上也需要以下幾個(gè)階段:生成,收集,注冊(cè),鏈接??傮w的流程比較繁雜,因此本文首先開始介紹第一階段,生成。在生成階段,UHT分析我們的代碼,并生成類型系統(tǒng)的相關(guān)代碼。

Note1:生成的代碼和注冊(cè)的過程會(huì)因?yàn)镠otReload功能的開啟與否有些不一樣,因此為了最簡(jiǎn)化流程闡述,我們先關(guān)閉HotReload,關(guān)閉的方式是在Hello.Build.cs里加上一行:Definitions.Add("WITH_HOT_RELOAD_CTORS=0");
Note2:本文開始及后續(xù)會(huì)簡(jiǎn)單的介紹一些用到的C++基礎(chǔ)知識(shí),但只是點(diǎn)到為止,不做深入探討。

C++ Static Lazy初始化模式

一種我們常用,也是UE中常用的單件懶惰初始化模式是:

Hello* StaticGetHello(){    static Hello* obj=nullptr;    if(!obj)
    {
        obj=...
    }    return obj;
}
或者Hello& StaticGetHello(){    static Hello obj(...);    return obj;
}

前者非常簡(jiǎn)單,也沒考慮多線程安全,但是在單線程環(huán)境下足夠用了。用指針的原因是,有一些情況,這些對(duì)象的生命周期是由別的地方來管理的,比如UE里的GC,因此這里只static化一個(gè)指針。否則的話,還是后者更加簡(jiǎn)潔和安全。

UHT代碼生成

在C++程序中的預(yù)處理是用來對(duì)源代碼進(jìn)行宏展開,預(yù)編譯指令處理,注釋刪除等操作。同樣的,一旦我們采用了宏標(biāo)記的方法,不管是怎么個(gè)標(biāo)記語法,我們都需要進(jìn)行簡(jiǎn)單或復(fù)雜的詞法分析,提取出有用的信息,然后生成所需要的代碼。在引擎里創(chuàng)建一個(gè)空C++項(xiàng)目命名為Hello,然后創(chuàng)建個(gè)不繼承的MyClass類。編譯,UHT就會(huì)為我們生成以下4個(gè)文件(位于Hello\Intermediate\Build\Win64\Hello\Inc\Hello)

  • HelloClasses.h:目前無用

  • MyClass.generated.h:MyClass的生成頭文件

  • Hello.generated.dep.h:Hello.generated.cpp的依賴頭文件,也就是順序包含上述的MyClass.h而已

  • Hello.generated.cpp:該項(xiàng)目的實(shí)現(xiàn)編譯單元。

其生成的文件初看起來很多很復(fù)雜,但其實(shí)比較簡(jiǎn)單,不過就是一些宏替換而已。生成的函數(shù)大都也以Z_開頭,筆者開始也在猜想Z_前綴的縮寫含義,感謝NetFly向Epic的人求證之后的回答:

The 'Z_' prefix is not part of any official naming convention, and it
doesn't really mean anything. Some generated functions were named this way
to avoid name collisions and so that these functions will appear together at the
bottom of intelisense lists.

簡(jiǎn)而言之,沒什么特別含義,就是簡(jiǎn)單為了避免命名沖突,用Z是為了字母排序總是出現(xiàn)在智能感知的最下面,盡量隱藏起來。
接下來,請(qǐng)讀者們緊跟著我的步伐,開始進(jìn)行這趟剖析之旅。

UCLASS的生成代碼剖析

先從一個(gè)最簡(jiǎn)單的UMyClass的開始,總覽分析生成的代碼結(jié)構(gòu),接著再繼而觀察其他UEnum、UStruct、UInterface、UProperty、UFunction的代碼生成樣式。

MyClass.h

首先是我們自己編寫或者引擎幫我們生成的文件樣式:

// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "UObject/NoExportTypes.h"#include "MyClass.generated.h"UCLASS()class HELLO_API UMyClass : public UObject
{
    GENERATED_BODY()
};

第5行:#include "UObject/NoExportTypes.h" 通過查看文件內(nèi)容,發(fā)現(xiàn)這個(gè)文件在編譯的時(shí)候就是Include了其他一些更基礎(chǔ)的頭文件,比如#include "Math/Vector.h",因此你才能在MyClass里不用include就引用這些類。當(dāng)然,還有一些內(nèi)容是專門供UHT使用來生成藍(lán)圖類型的,現(xiàn)在暫時(shí)不需要管。

第6行:#include "MyClass.generated.h",就是為了引用生成的頭文件。這里請(qǐng)注意的是,該文件include位置在類聲明的前面,之后談到宏處理的時(shí)候會(huì)用到該信息。

第11行:GENERATED_BODY(),該宏是重中之重,其他的UCLASS宏只是提供信息,不參與編譯,而GENERATED_BODY正是把聲明和元數(shù)據(jù)定義關(guān)聯(lián)到一起的樞紐。繼續(xù)查看宏定義:

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

會(huì)發(fā)現(xiàn)GENERATED_BODY最終其實(shí)只是生成另外一個(gè)宏的名稱,因?yàn)椋?/p>

CURRENT_FILE_ID的定義是在MyClass.generated.h的89行:\#define CURRENT_FILE_ID Hello_Source_Hello_MyClass_h,這是UHT通過分析文件得到的信息。

__LINE__標(biāo)準(zhǔn)宏指向了該宏使用時(shí)候的的函數(shù),這里是11。加了一個(gè)__LINE__宏的目的是為了支持在同一個(gè)文件內(nèi)聲明多個(gè)類,比如在MyClass.h里接著再聲明UMyClass2,就可以支持生成不同的宏名稱。

因此總而生成的宏名稱是Hello_Source_Hello_MyClass_h_11_GENERATED_BODY,而這個(gè)宏就是定義在MyClass.generated.h的77行。值得一提的是,如果MyClass類需要UMyClass(const FObjectInitializer& ObjectInitializer)的構(gòu)造函數(shù)自定義實(shí)現(xiàn),則需要用GENERATED_UCLASS_BODY宏來讓最終生成的宏指向Hello_Source_Hello_MyClass_h_11_GENERATED_BODY_LEGACY(MyClass.generated.h的66行),其最終展開的內(nèi)容會(huì)多一個(gè)構(gòu)造函數(shù)的內(nèi)容實(shí)現(xiàn)。

MyClass.generated.h

UHT分析生成的文件內(nèi)容如下:

PRAGMA_DISABLE_DEPRECATION_WARNINGS#ifdef HELLO_MyClass_generated_h#error "MyClass.generated.h already included, missing '#pragma once' in MyClass.h"#endif#define HELLO_MyClass_generated_h#define Hello_Source_Hello_MyClass_h_11_RPC_WRAPPERS    //先忽略#define Hello_Source_Hello_MyClass_h_11_RPC_WRAPPERS_NO_PURE_DECLS  //先忽略#define Hello_Source_Hello_MyClass_h_11_INCLASS_NO_PURE_DECLS \    private: \    static void StaticRegisterNativesUMyClass(); \    friend HELLO_API class UClass* Z_Construct_UClass_UMyClass(); \    public: \    DECLARE_CLASS(UMyClass, UObject, COMPILED_IN_FLAGS(0), 0, TEXT("/Script/Hello"), NO_API) \    DECLARE_SERIALIZER(UMyClass) \    /** Indicates whether the class is compiled into the engine */ \    enum {IsIntrinsic=COMPILED_IN_INTRINSIC};#define Hello_Source_Hello_MyClass_h_11_INCLASS \    private: \    static void StaticRegisterNativesUMyClass(); \    friend HELLO_API class UClass* Z_Construct_UClass_UMyClass(); \    public: \    DECLARE_CLASS(UMyClass, UObject, COMPILED_IN_FLAGS(0), 0, TEXT("/Script/Hello"), NO_API) \    DECLARE_SERIALIZER(UMyClass) \    /** Indicates whether the class is compiled into the engine */ \    enum {IsIntrinsic=COMPILED_IN_INTRINSIC};#define Hello_Source_Hello_MyClass_h_11_STANDARD_CONSTRUCTORS \    /** Standard constructor, called after all reflected properties have been initialized */ \    NO_API UMyClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); \    DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyClass) \    DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, UMyClass); \DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyClass); \private: \    /** Private move- and copy-constructors, should never be used */ \    NO_API UMyClass(UMyClass&&); \    NO_API UMyClass(const UMyClass&); \public:#define Hello_Source_Hello_MyClass_h_11_ENHANCED_CONSTRUCTORS \    /** Standard constructor, called after all reflected properties have been initialized */ \    NO_API UMyClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { }; \private: \    /** Private move- and copy-constructors, should never be used */ \    NO_API UMyClass(UMyClass&&); \    NO_API UMyClass(const UMyClass&); \public: \    DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, UMyClass); \DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyClass); \    DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyClass)#define Hello_Source_Hello_MyClass_h_11_PRIVATE_PROPERTY_OFFSET     //先忽略#define Hello_Source_Hello_MyClass_h_8_PROLOG   //先忽略#define Hello_Source_Hello_MyClass_h_11_GENERATED_BODY_LEGACY \ //兩個(gè)重要的定義PRAGMA_DISABLE_DEPRECATION_WARNINGS \public: \
    Hello_Source_Hello_MyClass_h_11_PRIVATE_PROPERTY_OFFSET \
    Hello_Source_Hello_MyClass_h_11_RPC_WRAPPERS \
    Hello_Source_Hello_MyClass_h_11_INCLASS \
    Hello_Source_Hello_MyClass_h_11_STANDARD_CONSTRUCTORS \public: \
PRAGMA_ENABLE_DEPRECATION_WARNINGS#define Hello_Source_Hello_MyClass_h_11_GENERATED_BODY \    //兩個(gè)重要的定義PRAGMA_DISABLE_DEPRECATION_WARNINGS \public: \
    Hello_Source_Hello_MyClass_h_11_PRIVATE_PROPERTY_OFFSET \
    Hello_Source_Hello_MyClass_h_11_RPC_WRAPPERS_NO_PURE_DECLS \
    Hello_Source_Hello_MyClass_h_11_INCLASS_NO_PURE_DECLS \
    Hello_Source_Hello_MyClass_h_11_ENHANCED_CONSTRUCTORS \private: \
PRAGMA_ENABLE_DEPRECATION_WARNINGS#undef CURRENT_FILE_ID#define CURRENT_FILE_ID Hello_Source_Hello_MyClass_h    //前文說過的定義PRAGMA_ENABLE_DEPRECATION_WARNINGS

該文件都是宏定義,因?yàn)楹甓x是有前后順序的,因此咱們從尾向前看,請(qǐng)讀者此時(shí)和上文的代碼對(duì)照著看。
首先最底下是CURRENT_FILE_ID的定義

接著是兩個(gè)上文說過的GENERATED_BODY定義,先從最簡(jiǎn)單的結(jié)構(gòu)開始,不管那些PRIVATE_PROPERTY_OFFSET和PROLOG,以后會(huì)慢慢介紹到。這兩個(gè)宏接著包含了4個(gè)聲明在上面的其他宏。目前來說Hello_Source_Hello_MyClass_h_11_INCLASS和Hello_Source_Hello_MyClass_h_11_INCLASS_NO_PURE_DECLS的定義一模一樣,而Hello_Source_Hello_MyClass_h_11_STANDARD_CONSTRUCTORS和Hello_Source_Hello_MyClass_h_11_ENHANCED_CONSTRUCTORS的宏,如果讀者仔細(xì)查看對(duì)照的話,會(huì)發(fā)現(xiàn)二者只差了“: Super(ObjectInitializer) { }; ”構(gòu)造函數(shù)的默認(rèn)實(shí)現(xiàn)。

我們繼續(xù)往上,以Hello_Source_Hello_MyClass_h_11_ENHANCED_CONSTRUCTORS為例:

#define Hello_Source_Hello_MyClass_h_11_ENHANCED_CONSTRUCTORS \    /** Standard constructor, called after all reflected properties have been initialized */ \    NO_API UMyClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { }; \   //默認(rèn)的構(gòu)造函數(shù)實(shí)現(xiàn)private: \  //禁止掉C++11的移動(dòng)和拷貝構(gòu)造    /** Private move- and copy-constructors, should never be used */ \
    NO_API UMyClass(UMyClass&&); \    NO_API UMyClass(const UMyClass&); \public: \
    DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, UMyClass); \     //因?yàn)閃ITH_HOT_RELOAD_CTORS關(guān)閉,展開是空宏
    DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyClass); \   //同理,空宏
    DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyClass)

繼續(xù)查看DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL的定義:

#define DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(TClass) \    static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass(X); }

聲明定義了一個(gè)構(gòu)造函數(shù)包裝器。需要這么做的原因是,在根據(jù)名字反射創(chuàng)建對(duì)象的時(shí)候,需要調(diào)用該類的構(gòu)造函數(shù)??墒穷惖臉?gòu)造函數(shù)并不能用函數(shù)指針指向,因此這里就用一個(gè)static函數(shù)包裝一下,變成一個(gè)"平凡"的函數(shù)指針,而且所有類的簽名一致,就可以在UClass里用一個(gè)函數(shù)指針里保存起來。見引擎里Class.h的聲明:

class COREUOBJECT_API UClass : public UStruct
...
{
    ...    typedef void (*ClassConstructorType) (const FObjectInitializer&);
    ClassConstructorType ClassConstructor;
    ...
}

當(dāng)然,如果讀者需要自己實(shí)現(xiàn)一套反射框架的時(shí)候也可以采用更簡(jiǎn)潔的模式,采用模板實(shí)現(xiàn)也是異曲同工。

template<class TClass>void MyConstructor( const FObjectInitializer& X ){ 
    new((EInternal*)X.GetObj())TClass(X);
}

再繼續(xù)往上:

#define Hello_Source_Hello_MyClass_h_11_INCLASS \    private: \    static void StaticRegisterNativesUMyClass(); \  //定義在cpp中,目前都是空實(shí)現(xiàn)
    friend HELLO_API class UClass* Z_Construct_UClass_UMyClass(); \ //一個(gè)構(gòu)造該類UClass對(duì)象的輔助函數(shù)
    public: \
    DECLARE_CLASS(UMyClass, UObject, COMPILED_IN_FLAGS(0), 0, TEXT("/Script/Hello"), NO_API) \   //聲明該類的一些通用基本函數(shù)
    DECLARE_SERIALIZER(UMyClass) \  //聲明序列化函數(shù)    /** Indicates whether the class is compiled into the engine */ \
    enum {IsIntrinsic=COMPILED_IN_INTRINSIC};   //這個(gè)標(biāo)記指定了該類是C++Native類,不能動(dòng)態(tài)再改變,跟藍(lán)圖里構(gòu)造的動(dòng)態(tài)類進(jìn)行區(qū)分。

可以說DECLARE_CLASS是最重要的一個(gè)聲明,對(duì)照著定義:DECLARE_CLASS(UMyClass, UObject, COMPILED_IN_FLAGS(0), 0, TEXT("/Script/Hello"), NO_API)

  • TClass:類名 TSuperClass:基類名字

  • TStaticFlags:類的屬性標(biāo)記,這里是0,表示最默認(rèn),不帶任何其他屬性。讀者可以查看EClassFlags枚舉來查看其他定義。

  • TStaticCastFlags:指定了該類可以轉(zhuǎn)換為哪些類,這里為0表示不能轉(zhuǎn)為那些默認(rèn)的類,讀者可以自己查看EClassCastFlags聲明來查看具體有哪些默認(rèn)類轉(zhuǎn)換。

  • TPackage:類所處于的包名,所有的對(duì)象都必須處于一個(gè)包中,而每個(gè)包都具有一個(gè)名字,可以通過該名字來查找。這里是"/Script/Hello",指定是Script下的Hello,Script可以理解為用戶自己的實(shí)現(xiàn),不管是C++還是藍(lán)圖,都可以看作是引擎外的一種腳本,當(dāng)然用這個(gè)名字也肯定有UE3時(shí)代UnrealScript的影子。Hello就是項(xiàng)目名字,該項(xiàng)目下定義的對(duì)象處于該包中。Package的概念涉及到后續(xù)Object的組織方式,目前可以簡(jiǎn)單理解為一個(gè)大的Object包含了其他子Object。

  • TRequiredAPI:就是用來Dll導(dǎo)入導(dǎo)出的標(biāo)記,這里是NO_API,因?yàn)槭亲罱Kexe,不需要導(dǎo)出。

#define DECLARE_CLASS( TClass, TSuperClass, TStaticFlags, TStaticCastFlags, TPackage, TRequiredAPI  ) \private: \    TClass& operator=(TClass&&);   \    TClass& operator=(const TClass&);   \    TRequiredAPI static UClass* GetPrivateStaticClass(const TCHAR* Package); \public: \    /** Bitwise union of #EClassFlags pertaining to this class.*/ \    enum {StaticClassFlags=TStaticFlags}; \    /** Typedef for the base class ({{ typedef-type }}) */ \    typedef TSuperClass Super;\    /** Typedef for {{ typedef-type }}. */ \    typedef TClass ThisClass;\    /** Returns a UClass object representing this class at runtime */ \    inline static UClass* StaticClass() \    { \        return GetPrivateStaticClass(TPackage); \    } \    /** Returns the StaticClassFlags for this class */ \    inline static EClassCastFlags StaticClassCastFlags() \    { \        return TStaticCastFlags; \    } \    DEPRECATED(4.7, "operator new has been deprecated for UObjects - please use NewObject or NewNamedObject instead") \    inline void* operator new( const size_t InSize, UObject* InOuter=(UObject*)GetTransientPackage(), FName InName=NAME_None, EObjectFlags InSetFlags=RF_NoFlags ) \    { \        return StaticAllocateObject( StaticClass(), InOuter, InName, InSetFlags ); \    } \    /** For internal use only; use StaticConstructObject() to create new objects. */ \    inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags) \    { \        return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags); \} \    /** For internal use only; use StaticConstructObject() to create new objects. */ \    inline void* operator new( const size_t InSize, EInternal* InMem ) \    { \        return (void*)InMem; \    }

大部分都是不言自明的,這里的StaticClass就是我們最經(jīng)常用到的函數(shù),其內(nèi)部調(diào)用了GetPrivateStaticClass,而其實(shí)現(xiàn)正是在Hello.generated.cpp里的。

Hello.generated.cpp

而整個(gè)Hello項(xiàng)目會(huì)生成一個(gè)Hello.generated.cpp

#include "Hello.h"      //包含該項(xiàng)目的頭文件,繼而包含Engine.h#include "GeneratedCppIncludes.h"   //包含UObject模塊里一些必要頭文件#include "Hello.generated.dep.h"    //引用依賴文件,繼而include了MyClass.hPRAGMA_DISABLE_DEPRECATION_WARNINGSvoid EmptyLinkFunctionForGeneratedCode1Hello() {}   //先忽略
    void UMyClass::StaticRegisterNativesUMyClass()  //說是靜態(tài)注冊(cè),但現(xiàn)在都是為空,先忽略
    {
    }
    IMPLEMENT_CLASS(UMyClass, 899540749);   //重要?。?!#if USE_COMPILED_IN_NATIVES //該宏編譯的時(shí)候會(huì)打開// Cross Module References
    COREUOBJECT_API class UClass* Z_Construct_UClass_UObject(); //引用CoreUObject里的函數(shù),主要是為了得到UObject本身對(duì)應(yīng)的UClass

    HELLO_API class UClass* Z_Construct_UClass_UMyClass_NoRegister();   //構(gòu)造UMyClass對(duì)應(yīng)的UClass對(duì)象,區(qū)別是沒有后續(xù)的注冊(cè)過程
    HELLO_API class UClass* Z_Construct_UClass_UMyClass();  //構(gòu)造UMyClass對(duì)應(yīng)的UClass對(duì)象
    HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello(); //構(gòu)造Hello本身的UPackage對(duì)象
    UClass* Z_Construct_UClass_UMyClass_NoRegister()
    {        return UMyClass::StaticClass(); //直接通過訪問來獲取UClass對(duì)象
    }    UClass* Z_Construct_UClass_UMyClass()   //構(gòu)造并注冊(cè)
    {        static UClass* OuterClass = NULL;   //static lazy模式
        if (!OuterClass)
        {
            Z_Construct_UClass_UObject();   //確保UObject本身的UClass已經(jīng)注冊(cè)生成
            Z_Construct_UPackage__Script_Hello();   //確保當(dāng)前Hello項(xiàng)目的UPackage已經(jīng)創(chuàng)建,因?yàn)楹罄m(xù)在生成UMyClass的UClass*對(duì)象時(shí)需要保存在這個(gè)UPacage中
            OuterClass = UMyClass::StaticClass();   //訪問獲得UClass*
            if (!(OuterClass->ClassFlags & CLASS_Constructed))  //防止重復(fù)注冊(cè)
            {
                UObjectForceRegistration(OuterClass);   //提取信息注冊(cè)自身
                OuterClass->ClassFlags |= 0x20100080;   //增加CLASS_Constructed|CLASS_RequiredAPI標(biāo)記


                OuterClass->StaticLink();   //“靜態(tài)”鏈接,后續(xù)解釋#if WITH_METADATA   //編輯器模式下開始
                UMetaData* MetaData = OuterClass->GetOutermost()->GetMetaData();    //獲取關(guān)聯(lián)到的UPackage其身上的元數(shù)據(jù)映射,并增加元數(shù)據(jù)信息
                MetaData->SetValue(OuterClass, TEXT("IncludePath"), TEXT("MyClass.h"));
                MetaData->SetValue(OuterClass, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));#endif
            }
        }
        check(OuterClass->GetClass());        return OuterClass;
    }    static FCompiledInDefer Z_CompiledInDefer_UClass_UMyClass(Z_Construct_UClass_UMyClass, &UMyClass::StaticClass, TEXT("UMyClass"), false, nullptr, nullptr, nullptr);    //延遲注冊(cè),注入信息,在啟動(dòng)的時(shí)候調(diào)用
    DEFINE_VTABLE_PTR_HELPER_CTOR(UMyClass);    //HotReload相關(guān),先忽略
    UPackage* Z_Construct_UPackage__Script_Hello()  //構(gòu)造Hello的UPackage
    {        static UPackage* ReturnPackage = NULL;        if (!ReturnPackage)
        {
            ReturnPackage = CastChecked<UPackage>(StaticFindObjectFast(UPackage::StaticClass(), NULL, FName(TEXT("/Script/Hello")), false, false));//注意的是這里只是做一個(gè)查找,真正的CreatePackage是在UObjectBase::DeferredRegister里調(diào)用的,后續(xù)在流程里會(huì)討論到
            ReturnPackage->SetPackageFlags(PKG_CompiledIn | 0x00000000);//設(shè)定標(biāo)記和Guid
            FGuid Guid;
            Guid.A = 0x79A097CD;
            Guid.B = 0xB58D8B48;
            Guid.C = 0x00000000;
            Guid.D = 0x00000000;
            ReturnPackage->SetGuid(Guid);

        }        return ReturnPackage;
    }#endifPRAGMA_ENABLE_DEPRECATION_WARNINGS

大部分簡(jiǎn)單的都注釋說明了,本文件的關(guān)鍵點(diǎn)在于IMPLEMENT_CLASS的分析,和上文.h中的DECLARE_CLASS對(duì)應(yīng),其聲明如下:
對(duì)照著定義IMPLEMENT_CLASS(UMyClass, 899540749);

#define IMPLEMENT_CLASS(TClass, TClassCrc) \    static TClassCompiledInDefer<TClass> AutoInitialize##TClass(TEXT(#TClass), sizeof(TClass), TClassCrc); \   //延遲注冊(cè)
    UClass* TClass::GetPrivateStaticClass(const TCHAR* Package) \   //.h里聲明的實(shí)現(xiàn),StaticClas()內(nèi)部就是調(diào)用該函數(shù)
    { \        static UClass* PrivateStaticClass = NULL; \ //又一次static lazy
        if (!PrivateStaticClass) \
        { \            /* this could be handled with templates, but we want it external to avoid code bloat */ \
            GetPrivateStaticClassBody( \    //該函數(shù)就是真正創(chuàng)建UClass*,以后
                Package, \  //Package名字
                (TCHAR*)TEXT(#TClass) + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0), \//類名,+1去掉U、A、F前綴,+11去掉_Deprecated前綴
                PrivateStaticClass, \   //輸出引用
                StaticRegisterNatives##TClass, \                sizeof(TClass), \
                TClass::StaticClassFlags, \
                TClass::StaticClassCastFlags(), \
                TClass::StaticConfigName(), \
                (UClass::ClassConstructorType)InternalConstructor<TClass>, \
                (UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<TClass>, \
                &TClass::AddReferencedObjects, \
                &TClass::Super::StaticClass, \
                &TClass::WithinClass::StaticClass \
            ); \
        } \        return PrivateStaticClass; \
    }

內(nèi)容也比較簡(jiǎn)單,就是把該類的信息傳進(jìn)去給GetPrivateStaticClassBody函數(shù)。

最后展開結(jié)果

通過人肉預(yù)處理展開一下生成的文件,應(yīng)該會(huì)看得更加清楚一些:
MyClass.h展開

#pragma once#include "UObject/NoExportTypes.h"class HELLO_API UMyClass : public UObject
{private:    static void StaticRegisterNativesUMyClass();    friend HELLO_API class UClass* Z_Construct_UClass_UMyClass();private:
    UMyClass& operator=(UMyClass&&);
    UMyClass& operator=(const UMyClass&);    NO_API static UClass* GetPrivateStaticClass(const TCHAR* Package);public:    /** Bitwise union of #EClassFlags pertaining to this class.*/
    enum {StaticClassFlags = CLASS_Intrinsic};    /** Typedef for the base class ({{ typedef-type }}) */
    typedef UObject Super;    /** Typedef for {{ typedef-type }}. */
    typedef UMyClass ThisClass;    /** Returns a UClass object representing this class at runtime */
    inline static UClass* StaticClass()
    {        return GetPrivateStaticClass(TEXT("/Script/Hello"));
    }    /** Returns the StaticClassFlags for this class */
    inline static EClassCastFlags StaticClassCastFlags()
    {        return 0;
    }
    DEPRECATED(4.7, "operator new has been deprecated for UObjects - please use NewObject or NewNamedObject instead")
    inline void* operator new(const size_t InSize, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags)
    {        return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags);
    }    /** For internal use only; use StaticConstructObject() to create new objects. */
    inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags)
    {        return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags);
    }    /** For internal use only; use StaticConstructObject() to create new objects. */
    inline void* operator new(const size_t InSize, EInternal* InMem)
    {        return (void*)InMem;
    }    friend FArchive &operator<<(FArchive& Ar, UMyClass*& Res)
    {        return Ar << (UObject*&)Res;
    }    /** Indicates whether the class is compiled into the engine */
    enum { IsIntrinsic = COMPILED_IN_INTRINSIC };    /** Standard constructor, called after all reflected properties have been initialized */
    NO_API UMyClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { };private:    /** Private move- and copy-constructors, should never be used */
    NO_API UMyClass(UMyClass&&);    NO_API UMyClass(const UMyClass&);public:    static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())UMyClass(X); }
};

Hello.generated.cpp展開

//#include "Hello.h"#include "Engine.h" //#include "GeneratedCppIncludes.h"#include "CoreUObject.h"#include "UObject/Object.h"#include "UObject/Class.h"#include "UObject/Package.h"#include "UObject/MetaData.h"#include "UObject/UnrealType.h"//#include "Hello.generated.dep.h"#include "MyClass.h"void EmptyLinkFunctionForGeneratedCode1Hello() {}void UMyClass::StaticRegisterNativesUMyClass()
{
}static TClassCompiledInDefer<UMyClass> AutoInitializeUMyClass(TEXT("UMyClass"), sizeof(UMyClass), 899540749);
UClass* UMyClass::GetPrivateStaticClass(const TCHAR* Package)
{    static UClass* PrivateStaticClass = NULL;    if (!PrivateStaticClass)
    {        /* this could be handled with templates, but we want it external to avoid code bloat */
        GetPrivateStaticClassBody(
            Package,
            (TCHAR*)TEXT("UMyClass") + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0),
            PrivateStaticClass,
            StaticRegisterNativesUMyClass,            sizeof(UMyClass),
            UMyClass::StaticClassFlags,
            UMyClass::StaticClassCastFlags(),
            UMyClass::StaticConfigName(),
            (UClass::ClassConstructorType)InternalConstructor<UMyClass>,
            (UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<UMyClass>,
            &UMyClass::AddReferencedObjects,
            &UMyClass::Super::StaticClass,
            &UMyClass::WithinClass::StaticClass
        );
    }    return PrivateStaticClass;
}// Cross Module ReferencesCOREUOBJECT_API class UClass* Z_Construct_UClass_UObject();HELLO_API class UClass* Z_Construct_UClass_UMyClass_NoRegister();HELLO_API class UClass* Z_Construct_UClass_UMyClass();HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello();UClass* Z_Construct_UClass_UMyClass_NoRegister(){    return UMyClass::StaticClass();
}UClass* Z_Construct_UClass_UMyClass(){    static UClass* OuterClass = NULL;    if (!OuterClass)
    {
        Z_Construct_UClass_UObject();
        Z_Construct_UPackage__Script_Hello();
        OuterClass = UMyClass::StaticClass();        if (!(OuterClass->ClassFlags & CLASS_Constructed))
        {
            UObjectForceRegistration(OuterClass);
            OuterClass->ClassFlags |= 0x20100080;


            OuterClass->StaticLink();#if WITH_METADATA
            UMetaData* MetaData = OuterClass->GetOutermost()->GetMetaData();
            MetaData->SetValue(OuterClass, TEXT("IncludePath"), TEXT("MyClass.h"));
            MetaData->SetValue(OuterClass, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));#endif
        }
    }
    check(OuterClass->GetClass());    return OuterClass;
}static FCompiledInDefer Z_CompiledInDefer_UClass_UMyClass(Z_Construct_UClass_UMyClass, &UMyClass::StaticClass, TEXT("UMyClass"), false, nullptr, nullptr, nullptr);UPackage* Z_Construct_UPackage__Script_Hello(){    static UPackage* ReturnPackage = NULL;    if (!ReturnPackage)
    {
        ReturnPackage = CastChecked<UPackage>(StaticFindObjectFast(UPackage::StaticClass(), NULL, FName(TEXT("/Script/Hello")), false, false));
        ReturnPackage->SetPackageFlags(PKG_CompiledIn | 0x00000000);
        FGuid Guid;
        Guid.A = 0x79A097CD;
        Guid.B = 0xB58D8B48;
        Guid.C = 0x00000000;
        Guid.D = 0x00000000;
        ReturnPackage->SetGuid(Guid);

    }    return ReturnPackage;
}

這樣.h的聲明和.cpp的定義就全都有了。不管定義了多少函數(shù),要記得注冊(cè)的入口就是那兩個(gè)static對(duì)象在程序啟動(dòng)的時(shí)候登記信息,才有了之后的注冊(cè)。

UENUM的生成代碼剖析

接著是相對(duì)簡(jiǎn)單的Enum,我們測(cè)試的Enum如下:

#pragma once#include "UObject/NoExportTypes.h"#include "MyEnum.generated.h"UENUM(BlueprintType)enum class EMyEnum : uint8
{    MY_Dance    UMETA(DisplayName = "Dance"),
    MY_Rain     UMETA(DisplayName = "Rain"),
    MY_Song     UMETA(DisplayName = "Song")};

生成的MyEnum.generated.h為:

PRAGMA_DISABLE_DEPRECATION_WARNINGS#ifdef HELLO_MyEnum_generated_h#error "MyEnum.generated.h already included, missing '#pragma once' in MyEnum.h"#endif#define HELLO_MyEnum_generated_h#undef CURRENT_FILE_ID#define CURRENT_FILE_ID Hello_Source_Hello_MyEnum_h#define FOREACH_ENUM_EMYENUM(op) \  //定義一個(gè)遍歷枚舉值的宏,只是為了方便使用
    op(EMyEnum::MY_Dance) \
    op(EMyEnum::MY_Rain) \
    op(EMyEnum::MY_Song) 
PRAGMA_ENABLE_DEPRECATION_WARNINGS

因此Enum也非常簡(jiǎn)單,所以發(fā)現(xiàn)生成的其實(shí)也沒有什么重要的信息。同樣的,生成的Hello.genrated.cpp中:

#include "Hello.h"#include "GeneratedCppIncludes.h"#include "Hello.generated.dep.h"PRAGMA_DISABLE_DEPRECATION_WARNINGSvoid EmptyLinkFunctionForGeneratedCode1Hello() {}static class UEnum* EMyEnum_StaticEnum()    //定義一個(gè)獲取UEnum便利函數(shù),會(huì)在延遲注冊(cè)的時(shí)候被用到{    extern HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello();    static class UEnum* Singleton = NULL;    if (!Singleton)
    {        extern HELLO_API class UEnum* Z_Construct_UEnum_Hello_EMyEnum();
        Singleton = GetStaticEnum(Z_Construct_UEnum_Hello_EMyEnum, Z_Construct_UPackage__Script_Hello(), TEXT("EMyEnum"));
    }    return Singleton;
}static FCompiledInDeferEnum Z_CompiledInDeferEnum_UEnum_EMyEnum(EMyEnum_StaticEnum, TEXT("/Script/Hello"), TEXT("EMyEnum"), false, nullptr, nullptr);   //延遲注冊(cè)#if USE_COMPILED_IN_NATIVES
    HELLO_API class UEnum* Z_Construct_UEnum_Hello_EMyEnum();    HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello();    UEnum* Z_Construct_UEnum_Hello_EMyEnum()    //構(gòu)造EMyEnum關(guān)聯(lián)的UEnum*
    {
        UPackage* Outer=Z_Construct_UPackage__Script_Hello();        extern uint32 Get_Z_Construct_UEnum_Hello_EMyEnum_CRC();        static UEnum* ReturnEnum = FindExistingEnumIfHotReloadOrDynamic(Outer, TEXT("EMyEnum"), 0, Get_Z_Construct_UEnum_Hello_EMyEnum_CRC(), false);        if (!ReturnEnum)
        {
            ReturnEnum = new(EC_InternalUseOnlyConstructor, Outer, TEXT("EMyEnum"), RF_Public|RF_Transient|RF_MarkAsNative) UEnum(FObjectInitializer());//直接創(chuàng)建該UEnum對(duì)象
            TArray<TPair<FName, uint8>> EnumNames;//設(shè)置枚舉里的名字和值
            EnumNames.Add(TPairInitializer<FName, uint8>(FName(TEXT("EMyEnum::MY_Dance")), 0));
            EnumNames.Add(TPairInitializer<FName, uint8>(FName(TEXT("EMyEnum::MY_Rain")), 1));
            EnumNames.Add(TPairInitializer<FName, uint8>(FName(TEXT("EMyEnum::MY_Song")), 2));
            EnumNames.Add(TPairInitializer<FName, uint8>(FName(TEXT("EMyEnum::MY_MAX")), 3));   //添加一個(gè)默認(rèn)的MAX字段
            ReturnEnum->SetEnums(EnumNames, UEnum::ECppForm::EnumClass);
            ReturnEnum->CppType = TEXT("EMyEnum");#if WITH_METADATA   //設(shè)置元數(shù)據(jù)
            UMetaData* MetaData = ReturnEnum->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnEnum, TEXT("BlueprintType"), TEXT("true"));
            MetaData->SetValue(ReturnEnum, TEXT("ModuleRelativePath"), TEXT("MyEnum.h"));
            MetaData->SetValue(ReturnEnum, TEXT("MY_Dance.DisplayName"), TEXT("Dance"));
            MetaData->SetValue(ReturnEnum, TEXT("MY_Rain.DisplayName"), TEXT("Rain"));
            MetaData->SetValue(ReturnEnum, TEXT("MY_Song.DisplayName"), TEXT("Song"));#endif
        }        return ReturnEnum;
    }    uint32 Get_Z_Construct_UEnum_Hello_EMyEnum_CRC() { return 2000113000U; }    UPackage* Z_Construct_UPackage__Script_Hello()  //設(shè)置Hello項(xiàng)目的Package屬性
    {
        ...略
    }#endifPRAGMA_ENABLE_DEPRECATION_WARNINGS

觀察發(fā)現(xiàn)EMyEnum_StaticEnum其實(shí)并沒有比Z_Construct_UEnum_Hello_EMyEnum實(shí)現(xiàn)更多的其他的功能。GetStaticEnum目前的實(shí)現(xiàn)內(nèi)部也只是非常簡(jiǎn)單的調(diào)用Z_Construct_UEnum_Hello_EMyEnum返回結(jié)果。所以保留著這個(gè)EMyEnum_StaticEnum或許只是為了和UClass的結(jié)構(gòu)保持一致。

USTRUCT的生成代碼剖析

因?yàn)閁STRUCT標(biāo)記的類內(nèi)部并不能定義函數(shù),因此測(cè)試的Struct如下:

#pragma once#include "UObject/NoExportTypes.h"#include "MyStruct.generated.h"USTRUCT(BlueprintType)struct HELLO_API FMyStruct
{
    GENERATED_USTRUCT_BODY()

    UPROPERTY(BlueprintReadWrite)    float Score;
};

生成的MyStruct.generated.h如下:

PRAGMA_DISABLE_DEPRECATION_WARNINGS#ifdef HELLO_MyStruct_generated_h#error "MyStruct.generated.h already included, missing '#pragma once' in MyStruct.h"#endif#define HELLO_MyStruct_generated_h#define Hello_Source_Hello_MyStruct_h_8_GENERATED_BODY \    friend HELLO_API class UScriptStruct* Z_Construct_UScriptStruct_FMyStruct(); \  //給予全局方法友元權(quán)限
    static class UScriptStruct* StaticStruct(); //靜態(tài)函數(shù)返回UScriptStruct*#undef CURRENT_FILE_ID#define CURRENT_FILE_ID Hello_Source_Hello_MyStruct_hPRAGMA_ENABLE_DEPRECATION_WARNINGS

同理,根據(jù)GENERATED_USTRUCT_BODY的定義,最終會(huì)替換成Hello_Source_Hello_MyStruct_h_8_GENERATED_BODY宏。我們發(fā)現(xiàn)其實(shí)作用只是在內(nèi)部定義了一個(gè)StaticStruct函數(shù),因?yàn)镕MyStruct并不繼承于UObject,所以結(jié)構(gòu)也非常的簡(jiǎn)單。
再接著是Hello.genrated.cpp:

#include "Hello.h"#include "GeneratedCppIncludes.h"#include "Hello.generated.dep.h"PRAGMA_DISABLE_DEPRECATION_WARNINGSvoid EmptyLinkFunctionForGeneratedCode1Hello() {}class UScriptStruct* FMyStruct::StaticStruct()//實(shí)現(xiàn)了靜態(tài)獲取UScriptStruct*{    extern HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello();    static class UScriptStruct* Singleton = NULL;    if (!Singleton)
    {        extern HELLO_API class UScriptStruct* Z_Construct_UScriptStruct_FMyStruct();        extern HELLO_API uint32 Get_Z_Construct_UScriptStruct_FMyStruct_CRC();
        Singleton = GetStaticStruct(Z_Construct_UScriptStruct_FMyStruct, Z_Construct_UPackage__Script_Hello(), TEXT("MyStruct"), sizeof(FMyStruct), Get_Z_Construct_UScriptStruct_FMyStruct_CRC());
    }    return Singleton;
}static FCompiledInDeferStruct Z_CompiledInDeferStruct_UScriptStruct_FMyStruct(FMyStruct::StaticStruct, TEXT("/Script/Hello"), TEXT("MyStruct"), false, nullptr, nullptr);  //延遲注冊(cè)static struct FScriptStruct_Hello_StaticRegisterNativesFMyStruct
{
    FScriptStruct_Hello_StaticRegisterNativesFMyStruct()
    {
        UScriptStruct::DeferCppStructOps(FName(TEXT("MyStruct")),new UScriptStruct::TCppStructOps<FMyStruct>);
    }
} ScriptStruct_Hello_StaticRegisterNativesFMyStruct;    //static注冊(cè)#if USE_COMPILED_IN_NATIVES
    HELLO_API class UScriptStruct* Z_Construct_UScriptStruct_FMyStruct();    HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello();    UScriptStruct* Z_Construct_UScriptStruct_FMyStruct()    //構(gòu)造關(guān)聯(lián)的UScriptStruct*
    {
        UPackage* Outer = Z_Construct_UPackage__Script_Hello();        extern uint32 Get_Z_Construct_UScriptStruct_FMyStruct_CRC();        static UScriptStruct* ReturnStruct = FindExistingStructIfHotReloadOrDynamic(Outer, TEXT("MyStruct"), sizeof(FMyStruct), Get_Z_Construct_UScriptStruct_FMyStruct_CRC(), false);        if (!ReturnStruct)
        {
            ReturnStruct = new(EC_InternalUseOnlyConstructor, Outer, TEXT("MyStruct"), RF_Public|RF_Transient|RF_MarkAsNative) UScriptStruct(FObjectInitializer(), NULL, new UScriptStruct::TCppStructOps<FMyStruct>, EStructFlags(0x00000201));//直接創(chuàng)建UScriptStruct對(duì)象
            UProperty* NewProp_Score = new(EC_InternalUseOnlyConstructor, ReturnStruct, TEXT("Score"), RF_Public|RF_Transient|RF_MarkAsNative) UFloatProperty(CPP_PROPERTY_BASE(Score, FMyStruct), 0x0010000000000004);//直接關(guān)聯(lián)相應(yīng)的Property信息
            ReturnStruct->StaticLink(); //鏈接#if WITH_METADATA   //元數(shù)據(jù)
            UMetaData* MetaData = ReturnStruct->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnStruct, TEXT("BlueprintType"), TEXT("true"));
            MetaData->SetValue(ReturnStruct, TEXT("ModuleRelativePath"), TEXT("MyStruct.h"));
            MetaData->SetValue(NewProp_Score, TEXT("Category"), TEXT("MyStruct"));
            MetaData->SetValue(NewProp_Score, TEXT("ModuleRelativePath"), TEXT("MyStruct.h"));#endif
        }        return ReturnStruct;
    }    uint32 Get_Z_Construct_UScriptStruct_FMyStruct_CRC() { return 2914362188U; }    UPackage* Z_Construct_UPackage__Script_Hello()
    {
        ...略
    }#endifPRAGMA_ENABLE_DEPRECATION_WARNINGS

同理,會(huì)發(fā)現(xiàn)FMyStruct::StaticStruct內(nèi)部也不會(huì)比Z_Construct_UScriptStruct_FMyStruct更多的事情,GetStaticStruct的實(shí)現(xiàn)也只是簡(jiǎn)單的轉(zhuǎn)發(fā)到Z_Construct_UScriptStruct_FMyStruct。值得一提的是定義的ScriptStruct_Hello_StaticRegisterNativesFMyStruct,會(huì)在程序一啟動(dòng)就調(diào)用UScriptStruct::DeferCppStructOps向程序注冊(cè)該結(jié)構(gòu)的CPP信息(大小,內(nèi)存對(duì)齊等),和TClassCompiledInDefer<TClass>的作用相當(dāng)。FMyStruct的展開也是一目了然,就不再贅述了。

UINTERFACE的生成代碼剖析

UE對(duì)Interface也有支持,如果說FStruct就是一個(gè)純數(shù)據(jù)的POD容器,那么UInterface則是一個(gè)只能帶方法的純接口,比C++里的抽象類要根據(jù)的純粹一些。當(dāng)然這里談的都只涉及到用UPROPERTY和UFUNCTION宏標(biāo)記的那些,如果是純C++的字段和函數(shù),UE并不能管到那么寬。
測(cè)試的MyInterface.h為:

#pragma once#include "UObject/NoExportTypes.h"#include "MyInterface.generated.h"UINTERFACE(BlueprintType)class UMyInterface : public UInterface
{
    GENERATED_UINTERFACE_BODY()    
};class IMyInterface
{
    GENERATED_IINTERFACE_BODY()public:
    UFUNCTION(BlueprintImplementableEvent)    void BPFunc() const;
};

GENERATED_UINTERFACE_BODY和GENERATED_IINTERFACE_BODY都可以替換為GENERATED_BODY以提供一個(gè)默認(rèn)的UMyInterface(const FObjectInitializer& ObjectInitializer)構(gòu)造函數(shù)實(shí)現(xiàn)。不過GENERATED_IINTERFACE_BODY替換過后的效果也一樣,因?yàn)椴⒉恍枰敲匆粋€(gè)構(gòu)造函數(shù),所以用兩個(gè)都可以。
生成的MyInterface.generated.h如下:

PRAGMA_DISABLE_DEPRECATION_WARNINGS#ifdef HELLO_MyInterface_generated_h#error "MyInterface.generated.h already included, missing '#pragma once' in MyInterface.h"#endif#define HELLO_MyInterface_generated_h#define Hello_Source_Hello_MyInterface_h_8_RPC_WRAPPERS#define Hello_Source_Hello_MyInterface_h_8_RPC_WRAPPERS_NO_PURE_DECLS#define Hello_Source_Hello_MyInterface_h_8_EVENT_PARMSextern HELLO_API  FName HELLO_BPFunc;   //函數(shù)的名稱,在cpp中定義#define Hello_Source_Hello_MyInterface_h_8_CALLBACK_WRAPPERS#define Hello_Source_Hello_MyInterface_h_8_STANDARD_CONSTRUCTORS \    /** Standard constructor, called after all reflected properties have been initialized */ \    NO_API UMyInterface(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); \    DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyInterface) \    DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, UMyInterface); \DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyInterface); \private: \    /** Private move- and copy-constructors, should never be used */ \    NO_API UMyInterface(UMyInterface&&); \    NO_API UMyInterface(const UMyInterface&); \public:#define Hello_Source_Hello_MyInterface_h_8_ENHANCED_CONSTRUCTORS \    /** Standard constructor, called after all reflected properties have been initialized */ \    NO_API UMyInterface(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { }; \private: \    /** Private move- and copy-constructors, should never be used */ \    NO_API UMyInterface(UMyInterface&&); \    NO_API UMyInterface(const UMyInterface&); \public: \    DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, UMyInterface); \DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyInterface); \    DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyInterface)#undef GENERATED_UINTERFACE_BODY_COMMON#define GENERATED_UINTERFACE_BODY_COMMON() \    private: \    static void StaticRegisterNativesUMyInterface(); \  //注冊(cè)
    friend HELLO_API class UClass* Z_Construct_UClass_UMyInterface(); \ //構(gòu)造UClass*的方法public: \
    DECLARE_CLASS(UMyInterface, UInterface, COMPILED_IN_FLAGS(CLASS_Abstract | CLASS_Interface), 0, TEXT("/Script/Hello"), NO_API) \
    DECLARE_SERIALIZER(UMyInterface) \    enum {IsIntrinsic=COMPILED_IN_INTRINSIC};#define Hello_Source_Hello_MyInterface_h_8_GENERATED_BODY_LEGACY \        PRAGMA_DISABLE_DEPRECATION_WARNINGS \    GENERATED_UINTERFACE_BODY_COMMON() \    Hello_Source_Hello_MyInterface_h_8_STANDARD_CONSTRUCTORS \    PRAGMA_ENABLE_DEPRECATION_WARNINGS#define Hello_Source_Hello_MyInterface_h_8_GENERATED_BODY \    PRAGMA_DISABLE_DEPRECATION_WARNINGS \    GENERATED_UINTERFACE_BODY_COMMON() \    Hello_Source_Hello_MyInterface_h_8_ENHANCED_CONSTRUCTORS \private: \    PRAGMA_ENABLE_DEPRECATION_WARNINGS#define Hello_Source_Hello_MyInterface_h_8_INCLASS_IINTERFACE_NO_PURE_DECLS \protected: \    virtual ~IMyInterface() {} \public: \    typedef UMyInterface UClassType; \    static void Execute_BPFunc(const UObject* O); \    virtual UObject* _getUObject() const = 0;#define Hello_Source_Hello_MyInterface_h_8_INCLASS_IINTERFACE \protected: \    virtual ~IMyInterface() {} \public: \    typedef UMyInterface UClassType; \    static void Execute_BPFunc(const UObject* O); \    virtual UObject* _getUObject() const = 0;#define Hello_Source_Hello_MyInterface_h_5_PROLOG \    Hello_Source_Hello_MyInterface_h_8_EVENT_PARMS#define Hello_Source_Hello_MyInterface_h_13_GENERATED_BODY_LEGACY \PRAGMA_DISABLE_DEPRECATION_WARNINGS \public: \    Hello_Source_Hello_MyInterface_h_8_RPC_WRAPPERS \    Hello_Source_Hello_MyInterface_h_8_CALLBACK_WRAPPERS \    Hello_Source_Hello_MyInterface_h_8_INCLASS_IINTERFACE \public: \PRAGMA_ENABLE_DEPRECATION_WARNINGS#define Hello_Source_Hello_MyInterface_h_13_GENERATED_BODY \PRAGMA_DISABLE_DEPRECATION_WARNINGS \public: \    Hello_Source_Hello_MyInterface_h_8_RPC_WRAPPERS_NO_PURE_DECLS \    Hello_Source_Hello_MyInterface_h_8_CALLBACK_WRAPPERS \    Hello_Source_Hello_MyInterface_h_8_INCLASS_IINTERFACE_NO_PURE_DECLS \private: \PRAGMA_ENABLE_DEPRECATION_WARNINGS#undef CURRENT_FILE_ID#define CURRENT_FILE_ID Hello_Source_Hello_MyInterface_hPRAGMA_ENABLE_DEPRECATION_WARNINGS

因?yàn)榻涌诘亩x需要用到兩個(gè)類,所以生成的信息稍微繁復(fù)了一些。不過使用的時(shí)候,我們的類只是繼承于IMyInterface,UMyInerface只是作為一個(gè)接口類型的載體,用以區(qū)分和查找不同的接口。觀察的時(shí)候,也請(qǐng)注意行號(hào)的定義。
從底往上,最后兩個(gè)是IMyInterface里的宏展開,細(xì)看之后,會(huì)發(fā)現(xiàn)_LEGACY和正常版本并沒有差別。展開后是:

class IMyInterface
{protected: 
    virtual ~IMyInterface() {}  //禁止用接口指針釋放對(duì)象public: 
    typedef UMyInterface UClassType;    //設(shè)定UMyInterface為關(guān)聯(lián)的類型
    static void Execute_BPFunc(const UObject* O);   //藍(lán)圖調(diào)用的輔助函數(shù)
    virtual UObject* _getUObject() const = 0;   //public:
    UFUNCTION(BlueprintImplementableEvent)    void BPFunc() const;
};

再往上是UMyInterface的生成,因?yàn)閁MyInterface繼承于UObject的原因,所以也是從屬于Object系統(tǒng)的一份子,所以同樣需要遵循構(gòu)造函數(shù)的規(guī)則。UInterface本身其實(shí)也可以算是UClass的一種,所以生成的代碼跟UClass中的生成都差不多,區(qū)別是用了COMPILED_IN_FLAGS(CLASS_Abstract | CLASS_Interface)的不同標(biāo)記。有興趣的讀者可以自己展開看下。

生成的Hello.generated.cpp:

#include "Hello.h"#include "GeneratedCppIncludes.h"#include "Hello.generated.dep.h"PRAGMA_DISABLE_DEPRECATION_WARNINGSvoid EmptyLinkFunctionForGeneratedCode1Hello() {}
FName HELLO_BPFunc = FName(TEXT("BPFunc")); //名字的定義
    void IMyInterface::BPFunc() const   //讓編譯通過,同時(shí)加上錯(cuò)誤檢測(cè)
    {
        check(0 && "Do not directly call Event functions in Interfaces. Call Execute_BPFunc instead.");
    }    void UMyInterface::StaticRegisterNativesUMyInterface()
    {
    }
    IMPLEMENT_CLASS(UMyInterface, 4286549343);  //注冊(cè)類
    void IMyInterface::Execute_BPFunc(const UObject* O) //藍(lán)圖調(diào)用方法的實(shí)現(xiàn)
    {
        check(O != NULL);
        check(O->GetClass()->ImplementsInterface(UMyInterface::StaticClass()));//檢查是否實(shí)現(xiàn)了該接口
        UFunction* const Func = O->FindFunction(HELLO_BPFunc);  //通過名字找到方法
        if (Func)
        {            const_cast<UObject*>(O)->ProcessEvent(Func, NULL);  //在該對(duì)象上調(diào)用該方法
        }
    }#if USE_COMPILED_IN_NATIVES
    HELLO_API class UFunction* Z_Construct_UFunction_UMyInterface_BPFunc();    HELLO_API class UClass* Z_Construct_UClass_UMyInterface_NoRegister();    HELLO_API class UClass* Z_Construct_UClass_UMyInterface();    HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello();    UFunction* Z_Construct_UFunction_UMyInterface_BPFunc()//構(gòu)造BPFunc的UFunction
    {
        UObject* Outer=Z_Construct_UClass_UMyInterface();   //得到接口UMyInterface*對(duì)象
        static UFunction* ReturnFunction = NULL;        if (!ReturnFunction)
        {
            ReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT("BPFunc"), RF_Public|RF_Transient|RF_MarkAsNative) UFunction(FObjectInitializer(), NULL, 0x48020800, 65535); //直接構(gòu)造函數(shù)對(duì)象
            ReturnFunction->Bind(); //綁定到函數(shù)指針
            ReturnFunction->StaticLink();   //鏈接#if WITH_METADATA   //元數(shù)據(jù)
            UMetaData* MetaData = ReturnFunction->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnFunction, TEXT("ModuleRelativePath"), TEXT("MyInterface.h"));#endif
        }        return ReturnFunction;
    }    UClass* Z_Construct_UClass_UMyInterface_NoRegister()
    {        return UMyInterface::StaticClass();
    }    UClass* Z_Construct_UClass_UMyInterface()
    {        static UClass* OuterClass = NULL;        if (!OuterClass)
        {
            UInterface::StaticClass();  //確保基類UInterface已經(jīng)元數(shù)據(jù)構(gòu)造完成
            Z_Construct_UPackage__Script_Hello();
            OuterClass = UMyInterface::StaticClass();            if (!(OuterClass->ClassFlags & CLASS_Constructed))
            {
                UObjectForceRegistration(OuterClass);
                OuterClass->ClassFlags |= 0x20004081;//CLASS_Constructed|CLASS_Interface|CLASS_Native|CLASS_Abstract

                OuterClass->LinkChild(Z_Construct_UFunction_UMyInterface_BPFunc());//添加子字段

                OuterClass->AddFunctionToFunctionMapWithOverriddenName(Z_Construct_UFunction_UMyInterface_BPFunc(), "BPFunc"); // 1371259725 ,添加函數(shù)名字映射
                OuterClass->StaticLink();   //鏈接#if WITH_METADATA   //元數(shù)據(jù)
                UMetaData* MetaData = OuterClass->GetOutermost()->GetMetaData();
                MetaData->SetValue(OuterClass, TEXT("BlueprintType"), TEXT("true"));
                MetaData->SetValue(OuterClass, TEXT("ModuleRelativePath"), TEXT("MyInterface.h"));#endif
            }
        }
        check(OuterClass->GetClass());        return OuterClass;
    }    static FCompiledInDefer Z_CompiledInDefer_UClass_UMyInterface(Z_Construct_UClass_UMyInterface, &UMyInterface::StaticClass, TEXT("UMyInterface"), false, nullptr, nullptr, nullptr);    //延遲注冊(cè)
    DEFINE_VTABLE_PTR_HELPER_CTOR(UMyInterface);    UPackage* Z_Construct_UPackage__Script_Hello()
    {
        ...略
    }#endifPRAGMA_ENABLE_DEPRECATION_WARNINGS

基本和UClass中的結(jié)構(gòu)差不多,只是多了一些函數(shù)定義的過程和把函數(shù)添加到類中的操作。

UClass中的字段和函數(shù)生成代碼剖析

在最開始的時(shí)候,我們用了一個(gè)最簡(jiǎn)單的UMyClass來闡述整體的結(jié)構(gòu)。行百里者半九十,讓我們一鼓作氣,看看如果UMyClass里多了Property和Function之后又會(huì)起什么變化。
測(cè)試的MyClass.h如下:

#pragma once#include "UObject/NoExportTypes.h"#include "MyClass.generated.h"UCLASS(BlueprintType)class HELLO_API UMyClass : public UObject
{
    GENERATED_BODY()public:
    UPROPERTY(BlueprintReadWrite)    float Score;public:
    UFUNCTION(BlueprintCallable, Category = "Hello")    void CallableFunc();    //C++實(shí)現(xiàn),藍(lán)圖調(diào)用

    UFUNCTION(BlueprintNativeEvent, Category = "Hello")    void NativeFunc();  //C++實(shí)現(xiàn)默認(rèn)版本,藍(lán)圖可重載實(shí)現(xiàn)

    UFUNCTION(BlueprintImplementableEvent, Category = "Hello")    void ImplementableFunc();   //C++不實(shí)現(xiàn),藍(lán)圖實(shí)現(xiàn)};

增加了一個(gè)屬性和三個(gè)不同方法來測(cè)試。
其生成的MyClass.generated.h為(只包括改變部分):

#define Hello_Source_Hello_MyClass_h_8_RPC_WRAPPERS \    virtual void NativeFunc_Implementation(); \ //默認(rèn)實(shí)現(xiàn)的函數(shù)聲明,我們可以自己實(shí)現(xiàn)
 \
    DECLARE_FUNCTION(execNativeFunc) \  //聲明供藍(lán)圖調(diào)用的函數(shù)
    { \
        P_FINISH; \
        P_NATIVE_BEGIN; \        this->NativeFunc_Implementation(); \
        P_NATIVE_END; \
    } \
 \
    DECLARE_FUNCTION(execCallableFunc) \    //聲明供藍(lán)圖調(diào)用的函數(shù)
    { \
        P_FINISH; \
        P_NATIVE_BEGIN; \        this->CallableFunc(); \
        P_NATIVE_END; \
    }#define Hello_Source_Hello_MyClass_h_8_RPC_WRAPPERS_NO_PURE_DECLS \ //和上面重復(fù),略//聲明函數(shù)名稱extern HELLO_API  FName HELLO_ImplementableFunc;extern HELLO_API  FName HELLO_NativeFunc;

因?yàn)镃allableFunc是C++里實(shí)現(xiàn)的,所以這里并不需要再定義函數(shù)體。而另外兩個(gè)函數(shù)其實(shí)是在藍(lán)圖里定義的,就需要專門生成exec前綴的函數(shù)供藍(lán)圖虛擬機(jī)調(diào)用。
我們展開execCallableFunc后為:

void execCallableFunc( FFrame& Stack, void*const Z_Param__Result )  //藍(lán)圖虛擬機(jī)的使用的函數(shù)接口{
    Stack.Code += !!Stack.Code; /* increment the code ptr unless it is null */
    { 
        FBlueprintEventTimer::FScopedNativeTimer ScopedNativeCallTimer;     //藍(lán)圖的計(jì)時(shí)統(tǒng)計(jì)
        this->CallableFunc(); //調(diào)用我們自己的實(shí)現(xiàn)
    }
}

目前還是非常簡(jiǎn)單的,當(dāng)然根據(jù)函數(shù)簽名的不同會(huì)加上不同的參數(shù)傳遞,但是大體結(jié)構(gòu)就是如此。以上的函數(shù)都是定義在UMyClass類內(nèi)部的。
再來看Hello.generated.cpp里的變化(只包括改變部分):

//函數(shù)名字定義FName HELLO_ImplementableFunc = FName(TEXT("ImplementableFunc"));
FName HELLO_NativeFunc = FName(TEXT("NativeFunc"));    void UMyClass::ImplementableFunc()  //C++端的實(shí)現(xiàn)
    {
        ProcessEvent(FindFunctionChecked(HELLO_ImplementableFunc),NULL);
    }    void UMyClass::NativeFunc() //C++端的實(shí)現(xiàn)
    {
        ProcessEvent(FindFunctionChecked(HELLO_NativeFunc),NULL);
    }    void UMyClass::StaticRegisterNativesUMyClass()  //注冊(cè)函數(shù)名字和函數(shù)指針映射
    {
        FNativeFunctionRegistrar::RegisterFunction(UMyClass::StaticClass(), "CallableFunc",(Native)&UMyClass::execCallableFunc);
        FNativeFunctionRegistrar::RegisterFunction(UMyClass::StaticClass(), "NativeFunc",(Native)&UMyClass::execNativeFunc);
    }//...略去中間相同部分//構(gòu)造3個(gè)函數(shù)的UFunction*對(duì)象,結(jié)構(gòu)一樣,只是EFunctionFlags不一樣UFunction* Z_Construct_UFunction_UMyClass_CallableFunc()
    {
        UObject* Outer=Z_Construct_UClass_UMyClass();        static UFunction* ReturnFunction = NULL;        if (!ReturnFunction)
        {
            ReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT("CallableFunc"), RF_Public|RF_Transient|RF_MarkAsNative) UFunction(FObjectInitializer(), NULL, 0x04020401, 65535); //FUNC_BlueprintCallable|FUNC_Public|FUNC_Native|FUNC_Final
            ReturnFunction->Bind();
            ReturnFunction->StaticLink();#if WITH_METADATA
            UMetaData* MetaData = ReturnFunction->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnFunction, TEXT("Category"), TEXT("Hello"));
            MetaData->SetValue(ReturnFunction, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));#endif
        }        return ReturnFunction;
    }    UFunction* Z_Construct_UFunction_UMyClass_ImplementableFunc()
    {
        UObject* Outer=Z_Construct_UClass_UMyClass();        static UFunction* ReturnFunction = NULL;        if (!ReturnFunction)
        {
            ReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT("ImplementableFunc"), RF_Public|RF_Transient|RF_MarkAsNative) UFunction(FObjectInitializer(), NULL, 0x08020800, 65535); //FUNC_BlueprintEvent|FUNC_Public|FUNC_Event
            ReturnFunction->Bind();
            ReturnFunction->StaticLink();#if WITH_METADATA
            UMetaData* MetaData = ReturnFunction->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnFunction, TEXT("Category"), TEXT("Hello"));
            MetaData->SetValue(ReturnFunction, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));#endif
        }        return ReturnFunction;
    }    UFunction* Z_Construct_UFunction_UMyClass_NativeFunc()
    {
        UObject* Outer=Z_Construct_UClass_UMyClass();        static UFunction* ReturnFunction = NULL;        if (!ReturnFunction)
        {
            ReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT("NativeFunc"), RF_Public|RF_Transient|RF_MarkAsNative) UFunction(FObjectInitializer(), NULL, 0x08020C00, 65535);//FUNC_BlueprintEvent|FUNC_Public|FUNC_Event|FUNC_Native
            ReturnFunction->Bind();
            ReturnFunction->StaticLink();#if WITH_METADATA
            UMetaData* MetaData = ReturnFunction->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnFunction, TEXT("Category"), TEXT("Hello"));
            MetaData->SetValue(ReturnFunction, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));#endif
        }        return ReturnFunction;
    }//...略去中間相同部分UClass* Z_Construct_UClass_UMyClass()
    {        static UClass* OuterClass = NULL;        if (!OuterClass)
        {
            Z_Construct_UClass_UObject();
            Z_Construct_UPackage__Script_Hello();
            OuterClass = UMyClass::StaticClass();            if (!(OuterClass->ClassFlags & CLASS_Constructed))
            {
                UObjectForceRegistration(OuterClass);
                OuterClass->ClassFlags |= 0x20100080;                //添加子字段
                OuterClass->LinkChild(Z_Construct_UFunction_UMyClass_CallableFunc());
                OuterClass->LinkChild(Z_Construct_UFunction_UMyClass_ImplementableFunc());
                OuterClass->LinkChild(Z_Construct_UFunction_UMyClass_NativeFunc());

PRAGMA_DISABLE_DEPRECATION_WARNINGS
                UProperty* NewProp_Score = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("Score"), RF_Public|RF_Transient|RF_MarkAsNative) UFloatProperty(CPP_PROPERTY_BASE(Score, UMyClass), 0x0010000000000004);//添加屬性PRAGMA_ENABLE_DEPRECATION_WARNINGS                //添加函數(shù)名字映射
                OuterClass->AddFunctionToFunctionMapWithOverriddenName(Z_Construct_UFunction_UMyClass_CallableFunc(), "CallableFunc"); // 774395847
                OuterClass->AddFunctionToFunctionMapWithOverriddenName(Z_Construct_UFunction_UMyClass_ImplementableFunc(), "ImplementableFunc"); // 615168156
                OuterClass->AddFunctionToFunctionMapWithOverriddenName(Z_Construct_UFunction_UMyClass_NativeFunc(), "NativeFunc"); // 3085959641
                OuterClass->StaticLink();#if WITH_METADATA   //元數(shù)據(jù)
                UMetaData* MetaData = OuterClass->GetOutermost()->GetMetaData();
                MetaData->SetValue(OuterClass, TEXT("BlueprintType"), TEXT("true"));
                MetaData->SetValue(OuterClass, TEXT("IncludePath"), TEXT("MyClass.h"));
                MetaData->SetValue(OuterClass, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));
                MetaData->SetValue(NewProp_Score, TEXT("Category"), TEXT("MyClass"));
                MetaData->SetValue(NewProp_Score, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));#endif
            }
        }
        check(OuterClass->GetClass());        return OuterClass;
    }

可以看出,對(duì)于CallableFunc這種C++實(shí)現(xiàn),藍(lán)圖只是調(diào)用的方法,生成的代碼只是生成了相應(yīng)的UFunction*對(duì)象。而對(duì)于NativeFunc和ImplementableFunc,我們不會(huì)在C++里寫上它們的實(shí)現(xiàn),因此為了編譯通過,也為了可以從C++端直接調(diào)用,就需要在生成的代碼的時(shí)候也同樣生成一份默認(rèn)實(shí)現(xiàn)。
在之前的簡(jiǎn)單類生成代碼中,StaticRegisterNativesUMyClass總是空的,在這里UHT為它加上了把函數(shù)注冊(cè)進(jìn)程序內(nèi)存的操作。
3個(gè)函數(shù)的UFunction*生成,雖然它們的調(diào)用方式大相徑庭,但是生成的代碼的方式卻是結(jié)構(gòu)一致的,區(qū)別的只是不同的EFunctionFlags值。因此可以推斷出,更多的差異應(yīng)該是在藍(lán)圖虛擬機(jī)的部分實(shí)現(xiàn)的,該部分知識(shí)以后介紹藍(lán)圖的時(shí)候再討論。
最后,把1個(gè)屬性和3個(gè)方法添加進(jìn)UClass中,齊活收工。

總結(jié)

本篇篇幅較長(zhǎng),我們花了大量的敘述闡述UHT生成的代碼的樣式。首先從一個(gè)最簡(jiǎn)單的UMyClass開始,觀察整體生成代碼的結(jié)構(gòu),接著推進(jìn)到UMyEnum、UMyStruct、UMyInterface的代碼樣式,最后返歸到UMyClass在其中添加進(jìn)屬性和方法,觀察屬性和方法是怎么生成代碼和怎么和UClass*對(duì)象關(guān)聯(lián)起來的。其實(shí)我們也發(fā)現(xiàn),這個(gè)階段最重要的功能就是盡量的把程序的信息用代碼給記錄下來,對(duì)于Enum記下名字和值;對(duì)于Struct記下每個(gè)Property的名字和字節(jié)偏移;對(duì)于Interface記下每個(gè)函數(shù)或包裝函數(shù)的的函數(shù)指針和名字;對(duì)于Class同理都記下Property和Function。
當(dāng)然,我們現(xiàn)在只能涉及到一些最簡(jiǎn)單的屬性和方法類型,目的是讓讀者們對(duì)生成的代碼有個(gè)整體的概念,不要一下子陷入到了繁復(fù)的細(xì)節(jié)中去。觀察生成的代碼可知,其實(shí)就分兩部分,一是各種Z_輔助方法用來構(gòu)造出各種UClass*等對(duì)象;另一部分是都包含著一兩個(gè)static對(duì)象用來在程序啟動(dòng)的時(shí)候驅(qū)動(dòng)登記,繼而調(diào)用到前者的Z_方法,最終完成注冊(cè)。
在了解到了生成的代碼是什么樣之后,下篇,我們就將深入到這些注冊(cè)的流程中去。

題外話

我們也可以很容易的看出,UHT生成的這份代碼并不是最簡(jiǎn)潔的。比如生成的兩個(gè)宏,最終展開的結(jié)果卻一樣,又或者生成了空宏。這一方面原因固然是因?yàn)橛泻芏嗟臍v史遺留痕跡,另一方面也是因?yàn)樵趯?shí)現(xiàn)UHT的時(shí)候,為了照顧流程上的統(tǒng)一,并沒有精益求精的去優(yōu)化掉冗余的分支,只是保留了下來。想法是反正UHT生成的代碼,不是給開發(fā)者讀的,所以亂點(diǎn)也無所謂了。最重要的是It works!所以也就沒什么人有去改進(jìn)的動(dòng)力了。
C++的代碼生成,一般都免不了需要用到大量宏的配合。讀者們?nèi)绻雽?shí)現(xiàn)自己的代碼生成框架,筆者的建議是盡量的把公共的部分挪移到宏定義中去,并適當(dāng)?shù)睦媚0?,盡量最簡(jiǎn)潔化生成代碼的書寫方式,比如在UHT中的:

FGuid Guid;
Guid.A = 0xDF4B1A6D;
Guid.B = 0x02873257;
Guid.C = 0x00000000;
Guid.D = 0x00000000;//換成FGuid Guid(0xDF4B1A6D,0x02873257,0x00000000,0x00000000);就會(huì)簡(jiǎn)潔的多。

生成代碼雖然不經(jīng)常被人讀,但是在一些情況下添加腳本綁定,或者自己擴(kuò)展功能,有一個(gè)清晰漂亮的代碼生成樣式,無疑能大大減少理解成本。

PS:最近打算琢磨換工作,坐標(biāo)北京,方向UE,有合適的機(jī)會(huì),歡迎私信推薦。

引言

上文講到了UE的類型系統(tǒng)結(jié)構(gòu),以及UHT分析源碼的一些宏標(biāo)記設(shè)定。在已經(jīng)進(jìn)行了類型系統(tǒng)整體的設(shè)計(jì)之后,本文將開始討論接下來的步驟。暫時(shí)不討論UHT的細(xì)節(jié),假設(shè)UHT已經(jīng)分析得到了足夠的類型元數(shù)據(jù)信息,下一步就是利用這個(gè)信息在程序內(nèi)存中構(gòu)建起前文的類型系統(tǒng)結(jié)構(gòu),這個(gè)過程我們稱之為注冊(cè)。同一般程序的構(gòu)建流程需要經(jīng)過預(yù)處理、編譯、匯編、鏈接一樣,UE為了在內(nèi)存中模擬構(gòu)建的過程,在概念上也需要以下幾個(gè)階段:生成,收集,注冊(cè),鏈接??傮w的流程比較繁雜,因此本文首先開始介紹第一階段,生成。在生成階段,UHT分析我們的代碼,并生成類型系統(tǒng)的相關(guān)代碼。

Note1:生成的代碼和注冊(cè)的過程會(huì)因?yàn)镠otReload功能的開啟與否有些不一樣,因此為了最簡(jiǎn)化流程闡述,我們先關(guān)閉HotReload,關(guān)閉的方式是在Hello.Build.cs里加上一行:Definitions.Add("WITH_HOT_RELOAD_CTORS=0");
Note2:本文開始及后續(xù)會(huì)簡(jiǎn)單的介紹一些用到的C++基礎(chǔ)知識(shí),但只是點(diǎn)到為止,不做深入探討。

C++ Static Lazy初始化模式

一種我們常用,也是UE中常用的單件懶惰初始化模式是:

Hello* StaticGetHello(){    static Hello* obj=nullptr;    if(!obj)
    {
        obj=...
    }    return obj;
}
或者Hello& StaticGetHello(){    static Hello obj(...);    return obj;
}

前者非常簡(jiǎn)單,也沒考慮多線程安全,但是在單線程環(huán)境下足夠用了。用指針的原因是,有一些情況,這些對(duì)象的生命周期是由別的地方來管理的,比如UE里的GC,因此這里只static化一個(gè)指針。否則的話,還是后者更加簡(jiǎn)潔和安全。

UHT代碼生成

在C++程序中的預(yù)處理是用來對(duì)源代碼進(jìn)行宏展開,預(yù)編譯指令處理,注釋刪除等操作。同樣的,一旦我們采用了宏標(biāo)記的方法,不管是怎么個(gè)標(biāo)記語法,我們都需要進(jìn)行簡(jiǎn)單或復(fù)雜的詞法分析,提取出有用的信息,然后生成所需要的代碼。在引擎里創(chuàng)建一個(gè)空C++項(xiàng)目命名為Hello,然后創(chuàng)建個(gè)不繼承的MyClass類。編譯,UHT就會(huì)為我們生成以下4個(gè)文件(位于Hello\Intermediate\Build\Win64\Hello\Inc\Hello)

  • HelloClasses.h:目前無用

  • MyClass.generated.h:MyClass的生成頭文件

  • Hello.generated.dep.h:Hello.generated.cpp的依賴頭文件,也就是順序包含上述的MyClass.h而已

  • Hello.generated.cpp:該項(xiàng)目的實(shí)現(xiàn)編譯單元。

其生成的文件初看起來很多很復(fù)雜,但其實(shí)比較簡(jiǎn)單,不過就是一些宏替換而已。生成的函數(shù)大都也以Z_開頭,筆者開始也在猜想Z_前綴的縮寫含義,感謝NetFly向Epic的人求證之后的回答:

The 'Z_' prefix is not part of any official naming convention, and it
doesn't really mean anything. Some generated functions were named this way
to avoid name collisions and so that these functions will appear together at the
bottom of intelisense lists.

簡(jiǎn)而言之,沒什么特別含義,就是簡(jiǎn)單為了避免命名沖突,用Z是為了字母排序總是出現(xiàn)在智能感知的最下面,盡量隱藏起來。
接下來,請(qǐng)讀者們緊跟著我的步伐,開始進(jìn)行這趟剖析之旅。

UCLASS的生成代碼剖析

先從一個(gè)最簡(jiǎn)單的UMyClass的開始,總覽分析生成的代碼結(jié)構(gòu),接著再繼而觀察其他UEnum、UStruct、UInterface、UProperty、UFunction的代碼生成樣式。

MyClass.h

首先是我們自己編寫或者引擎幫我們生成的文件樣式:

// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "UObject/NoExportTypes.h"#include "MyClass.generated.h"UCLASS()class HELLO_API UMyClass : public UObject
{
    GENERATED_BODY()
};

第5行:#include "UObject/NoExportTypes.h" 通過查看文件內(nèi)容,發(fā)現(xiàn)這個(gè)文件在編譯的時(shí)候就是Include了其他一些更基礎(chǔ)的頭文件,比如#include "Math/Vector.h",因此你才能在MyClass里不用include就引用這些類。當(dāng)然,還有一些內(nèi)容是專門供UHT使用來生成藍(lán)圖類型的,現(xiàn)在暫時(shí)不需要管。

第6行:#include "MyClass.generated.h",就是為了引用生成的頭文件。這里請(qǐng)注意的是,該文件include位置在類聲明的前面,之后談到宏處理的時(shí)候會(huì)用到該信息。

第11行:GENERATED_BODY(),該宏是重中之重,其他的UCLASS宏只是提供信息,不參與編譯,而GENERATED_BODY正是把聲明和元數(shù)據(jù)定義關(guān)聯(lián)到一起的樞紐。繼續(xù)查看宏定義:

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

會(huì)發(fā)現(xiàn)GENERATED_BODY最終其實(shí)只是生成另外一個(gè)宏的名稱,因?yàn)椋?/p>

CURRENT_FILE_ID的定義是在MyClass.generated.h的89行:\#define CURRENT_FILE_ID Hello_Source_Hello_MyClass_h,這是UHT通過分析文件得到的信息。

__LINE__標(biāo)準(zhǔn)宏指向了該宏使用時(shí)候的的函數(shù),這里是11。加了一個(gè)__LINE__宏的目的是為了支持在同一個(gè)文件內(nèi)聲明多個(gè)類,比如在MyClass.h里接著再聲明UMyClass2,就可以支持生成不同的宏名稱。

因此總而生成的宏名稱是Hello_Source_Hello_MyClass_h_11_GENERATED_BODY,而這個(gè)宏就是定義在MyClass.generated.h的77行。值得一提的是,如果MyClass類需要UMyClass(const FObjectInitializer& ObjectInitializer)的構(gòu)造函數(shù)自定義實(shí)現(xiàn),則需要用GENERATED_UCLASS_BODY宏來讓最終生成的宏指向Hello_Source_Hello_MyClass_h_11_GENERATED_BODY_LEGACY(MyClass.generated.h的66行),其最終展開的內(nèi)容會(huì)多一個(gè)構(gòu)造函數(shù)的內(nèi)容實(shí)現(xiàn)。

MyClass.generated.h

UHT分析生成的文件內(nèi)容如下:

PRAGMA_DISABLE_DEPRECATION_WARNINGS#ifdef HELLO_MyClass_generated_h#error "MyClass.generated.h already included, missing '#pragma once' in MyClass.h"#endif#define HELLO_MyClass_generated_h#define Hello_Source_Hello_MyClass_h_11_RPC_WRAPPERS    //先忽略#define Hello_Source_Hello_MyClass_h_11_RPC_WRAPPERS_NO_PURE_DECLS  //先忽略#define Hello_Source_Hello_MyClass_h_11_INCLASS_NO_PURE_DECLS \    private: \    static void StaticRegisterNativesUMyClass(); \    friend HELLO_API class UClass* Z_Construct_UClass_UMyClass(); \    public: \    DECLARE_CLASS(UMyClass, UObject, COMPILED_IN_FLAGS(0), 0, TEXT("/Script/Hello"), NO_API) \    DECLARE_SERIALIZER(UMyClass) \    /** Indicates whether the class is compiled into the engine */ \    enum {IsIntrinsic=COMPILED_IN_INTRINSIC};#define Hello_Source_Hello_MyClass_h_11_INCLASS \    private: \    static void StaticRegisterNativesUMyClass(); \    friend HELLO_API class UClass* Z_Construct_UClass_UMyClass(); \    public: \    DECLARE_CLASS(UMyClass, UObject, COMPILED_IN_FLAGS(0), 0, TEXT("/Script/Hello"), NO_API) \    DECLARE_SERIALIZER(UMyClass) \    /** Indicates whether the class is compiled into the engine */ \    enum {IsIntrinsic=COMPILED_IN_INTRINSIC};#define Hello_Source_Hello_MyClass_h_11_STANDARD_CONSTRUCTORS \    /** Standard constructor, called after all reflected properties have been initialized */ \    NO_API UMyClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); \    DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyClass) \    DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, UMyClass); \DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyClass); \private: \    /** Private move- and copy-constructors, should never be used */ \    NO_API UMyClass(UMyClass&&); \    NO_API UMyClass(const UMyClass&); \public:#define Hello_Source_Hello_MyClass_h_11_ENHANCED_CONSTRUCTORS \    /** Standard constructor, called after all reflected properties have been initialized */ \    NO_API UMyClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { }; \private: \    /** Private move- and copy-constructors, should never be used */ \    NO_API UMyClass(UMyClass&&); \    NO_API UMyClass(const UMyClass&); \public: \    DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, UMyClass); \DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyClass); \    DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyClass)#define Hello_Source_Hello_MyClass_h_11_PRIVATE_PROPERTY_OFFSET     //先忽略#define Hello_Source_Hello_MyClass_h_8_PROLOG   //先忽略#define Hello_Source_Hello_MyClass_h_11_GENERATED_BODY_LEGACY \ //兩個(gè)重要的定義PRAGMA_DISABLE_DEPRECATION_WARNINGS \public: \
    Hello_Source_Hello_MyClass_h_11_PRIVATE_PROPERTY_OFFSET \
    Hello_Source_Hello_MyClass_h_11_RPC_WRAPPERS \
    Hello_Source_Hello_MyClass_h_11_INCLASS \
    Hello_Source_Hello_MyClass_h_11_STANDARD_CONSTRUCTORS \public: \
PRAGMA_ENABLE_DEPRECATION_WARNINGS#define Hello_Source_Hello_MyClass_h_11_GENERATED_BODY \    //兩個(gè)重要的定義PRAGMA_DISABLE_DEPRECATION_WARNINGS \public: \
    Hello_Source_Hello_MyClass_h_11_PRIVATE_PROPERTY_OFFSET \
    Hello_Source_Hello_MyClass_h_11_RPC_WRAPPERS_NO_PURE_DECLS \
    Hello_Source_Hello_MyClass_h_11_INCLASS_NO_PURE_DECLS \
    Hello_Source_Hello_MyClass_h_11_ENHANCED_CONSTRUCTORS \private: \
PRAGMA_ENABLE_DEPRECATION_WARNINGS#undef CURRENT_FILE_ID#define CURRENT_FILE_ID Hello_Source_Hello_MyClass_h    //前文說過的定義PRAGMA_ENABLE_DEPRECATION_WARNINGS

該文件都是宏定義,因?yàn)楹甓x是有前后順序的,因此咱們從尾向前看,請(qǐng)讀者此時(shí)和上文的代碼對(duì)照著看。
首先最底下是CURRENT_FILE_ID的定義

接著是兩個(gè)上文說過的GENERATED_BODY定義,先從最簡(jiǎn)單的結(jié)構(gòu)開始,不管那些PRIVATE_PROPERTY_OFFSET和PROLOG,以后會(huì)慢慢介紹到。這兩個(gè)宏接著包含了4個(gè)聲明在上面的其他宏。目前來說Hello_Source_Hello_MyClass_h_11_INCLASS和Hello_Source_Hello_MyClass_h_11_INCLASS_NO_PURE_DECLS的定義一模一樣,而Hello_Source_Hello_MyClass_h_11_STANDARD_CONSTRUCTORS和Hello_Source_Hello_MyClass_h_11_ENHANCED_CONSTRUCTORS的宏,如果讀者仔細(xì)查看對(duì)照的話,會(huì)發(fā)現(xiàn)二者只差了“: Super(ObjectInitializer) { }; ”構(gòu)造函數(shù)的默認(rèn)實(shí)現(xiàn)。

我們繼續(xù)往上,以Hello_Source_Hello_MyClass_h_11_ENHANCED_CONSTRUCTORS為例:

#define Hello_Source_Hello_MyClass_h_11_ENHANCED_CONSTRUCTORS \    /** Standard constructor, called after all reflected properties have been initialized */ \    NO_API UMyClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { }; \   //默認(rèn)的構(gòu)造函數(shù)實(shí)現(xiàn)private: \  //禁止掉C++11的移動(dòng)和拷貝構(gòu)造    /** Private move- and copy-constructors, should never be used */ \
    NO_API UMyClass(UMyClass&&); \    NO_API UMyClass(const UMyClass&); \public: \
    DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, UMyClass); \     //因?yàn)閃ITH_HOT_RELOAD_CTORS關(guān)閉,展開是空宏
    DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyClass); \   //同理,空宏
    DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyClass)

繼續(xù)查看DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL的定義:

#define DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(TClass) \    static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass(X); }

聲明定義了一個(gè)構(gòu)造函數(shù)包裝器。需要這么做的原因是,在根據(jù)名字反射創(chuàng)建對(duì)象的時(shí)候,需要調(diào)用該類的構(gòu)造函數(shù)??墒穷惖臉?gòu)造函數(shù)并不能用函數(shù)指針指向,因此這里就用一個(gè)static函數(shù)包裝一下,變成一個(gè)"平凡"的函數(shù)指針,而且所有類的簽名一致,就可以在UClass里用一個(gè)函數(shù)指針里保存起來。見引擎里Class.h的聲明:

class COREUOBJECT_API UClass : public UStruct
...
{
    ...    typedef void (*ClassConstructorType) (const FObjectInitializer&);
    ClassConstructorType ClassConstructor;
    ...
}

當(dāng)然,如果讀者需要自己實(shí)現(xiàn)一套反射框架的時(shí)候也可以采用更簡(jiǎn)潔的模式,采用模板實(shí)現(xiàn)也是異曲同工。

template<class TClass>void MyConstructor( const FObjectInitializer& X ){ 
    new((EInternal*)X.GetObj())TClass(X);
}

再繼續(xù)往上:

#define Hello_Source_Hello_MyClass_h_11_INCLASS \    private: \    static void StaticRegisterNativesUMyClass(); \  //定義在cpp中,目前都是空實(shí)現(xiàn)
    friend HELLO_API class UClass* Z_Construct_UClass_UMyClass(); \ //一個(gè)構(gòu)造該類UClass對(duì)象的輔助函數(shù)
    public: \
    DECLARE_CLASS(UMyClass, UObject, COMPILED_IN_FLAGS(0), 0, TEXT("/Script/Hello"), NO_API) \   //聲明該類的一些通用基本函數(shù)
    DECLARE_SERIALIZER(UMyClass) \  //聲明序列化函數(shù)    /** Indicates whether the class is compiled into the engine */ \
    enum {IsIntrinsic=COMPILED_IN_INTRINSIC};   //這個(gè)標(biāo)記指定了該類是C++Native類,不能動(dòng)態(tài)再改變,跟藍(lán)圖里構(gòu)造的動(dòng)態(tài)類進(jìn)行區(qū)分。

可以說DECLARE_CLASS是最重要的一個(gè)聲明,對(duì)照著定義:DECLARE_CLASS(UMyClass, UObject, COMPILED_IN_FLAGS(0), 0, TEXT("/Script/Hello"), NO_API)

  • TClass:類名 TSuperClass:基類名字

  • TStaticFlags:類的屬性標(biāo)記,這里是0,表示最默認(rèn),不帶任何其他屬性。讀者可以查看EClassFlags枚舉來查看其他定義。

  • TStaticCastFlags:指定了該類可以轉(zhuǎn)換為哪些類,這里為0表示不能轉(zhuǎn)為那些默認(rèn)的類,讀者可以自己查看EClassCastFlags聲明來查看具體有哪些默認(rèn)類轉(zhuǎn)換。

  • TPackage:類所處于的包名,所有的對(duì)象都必須處于一個(gè)包中,而每個(gè)包都具有一個(gè)名字,可以通過該名字來查找。這里是"/Script/Hello",指定是Script下的Hello,Script可以理解為用戶自己的實(shí)現(xiàn),不管是C++還是藍(lán)圖,都可以看作是引擎外的一種腳本,當(dāng)然用這個(gè)名字也肯定有UE3時(shí)代UnrealScript的影子。Hello就是項(xiàng)目名字,該項(xiàng)目下定義的對(duì)象處于該包中。Package的概念涉及到后續(xù)Object的組織方式,目前可以簡(jiǎn)單理解為一個(gè)大的Object包含了其他子Object。

  • TRequiredAPI:就是用來Dll導(dǎo)入導(dǎo)出的標(biāo)記,這里是NO_API,因?yàn)槭亲罱Kexe,不需要導(dǎo)出。

#define DECLARE_CLASS( TClass, TSuperClass, TStaticFlags, TStaticCastFlags, TPackage, TRequiredAPI  ) \private: \    TClass& operator=(TClass&&);   \    TClass& operator=(const TClass&);   \    TRequiredAPI static UClass* GetPrivateStaticClass(const TCHAR* Package); \public: \    /** Bitwise union of #EClassFlags pertaining to this class.*/ \    enum {StaticClassFlags=TStaticFlags}; \    /** Typedef for the base class ({{ typedef-type }}) */ \    typedef TSuperClass Super;\    /** Typedef for {{ typedef-type }}. */ \    typedef TClass ThisClass;\    /** Returns a UClass object representing this class at runtime */ \    inline static UClass* StaticClass() \    { \        return GetPrivateStaticClass(TPackage); \    } \    /** Returns the StaticClassFlags for this class */ \    inline static EClassCastFlags StaticClassCastFlags() \    { \        return TStaticCastFlags; \    } \    DEPRECATED(4.7, "operator new has been deprecated for UObjects - please use NewObject or NewNamedObject instead") \    inline void* operator new( const size_t InSize, UObject* InOuter=(UObject*)GetTransientPackage(), FName InName=NAME_None, EObjectFlags InSetFlags=RF_NoFlags ) \    { \        return StaticAllocateObject( StaticClass(), InOuter, InName, InSetFlags ); \    } \    /** For internal use only; use StaticConstructObject() to create new objects. */ \    inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags) \    { \        return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags); \} \    /** For internal use only; use StaticConstructObject() to create new objects. */ \    inline void* operator new( const size_t InSize, EInternal* InMem ) \    { \        return (void*)InMem; \    }

大部分都是不言自明的,這里的StaticClass就是我們最經(jīng)常用到的函數(shù),其內(nèi)部調(diào)用了GetPrivateStaticClass,而其實(shí)現(xiàn)正是在Hello.generated.cpp里的。

Hello.generated.cpp

而整個(gè)Hello項(xiàng)目會(huì)生成一個(gè)Hello.generated.cpp

#include "Hello.h"      //包含該項(xiàng)目的頭文件,繼而包含Engine.h#include "GeneratedCppIncludes.h"   //包含UObject模塊里一些必要頭文件#include "Hello.generated.dep.h"    //引用依賴文件,繼而include了MyClass.hPRAGMA_DISABLE_DEPRECATION_WARNINGSvoid EmptyLinkFunctionForGeneratedCode1Hello() {}   //先忽略
    void UMyClass::StaticRegisterNativesUMyClass()  //說是靜態(tài)注冊(cè),但現(xiàn)在都是為空,先忽略
    {
    }
    IMPLEMENT_CLASS(UMyClass, 899540749);   //重要!??!#if USE_COMPILED_IN_NATIVES //該宏編譯的時(shí)候會(huì)打開// Cross Module References
    COREUOBJECT_API class UClass* Z_Construct_UClass_UObject(); //引用CoreUObject里的函數(shù),主要是為了得到UObject本身對(duì)應(yīng)的UClass

    HELLO_API class UClass* Z_Construct_UClass_UMyClass_NoRegister();   //構(gòu)造UMyClass對(duì)應(yīng)的UClass對(duì)象,區(qū)別是沒有后續(xù)的注冊(cè)過程
    HELLO_API class UClass* Z_Construct_UClass_UMyClass();  //構(gòu)造UMyClass對(duì)應(yīng)的UClass對(duì)象
    HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello(); //構(gòu)造Hello本身的UPackage對(duì)象
    UClass* Z_Construct_UClass_UMyClass_NoRegister()
    {        return UMyClass::StaticClass(); //直接通過訪問來獲取UClass對(duì)象
    }    UClass* Z_Construct_UClass_UMyClass()   //構(gòu)造并注冊(cè)
    {        static UClass* OuterClass = NULL;   //static lazy模式
        if (!OuterClass)
        {
            Z_Construct_UClass_UObject();   //確保UObject本身的UClass已經(jīng)注冊(cè)生成
            Z_Construct_UPackage__Script_Hello();   //確保當(dāng)前Hello項(xiàng)目的UPackage已經(jīng)創(chuàng)建,因?yàn)楹罄m(xù)在生成UMyClass的UClass*對(duì)象時(shí)需要保存在這個(gè)UPacage中
            OuterClass = UMyClass::StaticClass();   //訪問獲得UClass*
            if (!(OuterClass->ClassFlags & CLASS_Constructed))  //防止重復(fù)注冊(cè)
            {
                UObjectForceRegistration(OuterClass);   //提取信息注冊(cè)自身
                OuterClass->ClassFlags |= 0x20100080;   //增加CLASS_Constructed|CLASS_RequiredAPI標(biāo)記


                OuterClass->StaticLink();   //“靜態(tài)”鏈接,后續(xù)解釋#if WITH_METADATA   //編輯器模式下開始
                UMetaData* MetaData = OuterClass->GetOutermost()->GetMetaData();    //獲取關(guān)聯(lián)到的UPackage其身上的元數(shù)據(jù)映射,并增加元數(shù)據(jù)信息
                MetaData->SetValue(OuterClass, TEXT("IncludePath"), TEXT("MyClass.h"));
                MetaData->SetValue(OuterClass, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));#endif
            }
        }
        check(OuterClass->GetClass());        return OuterClass;
    }    static FCompiledInDefer Z_CompiledInDefer_UClass_UMyClass(Z_Construct_UClass_UMyClass, &UMyClass::StaticClass, TEXT("UMyClass"), false, nullptr, nullptr, nullptr);    //延遲注冊(cè),注入信息,在啟動(dòng)的時(shí)候調(diào)用
    DEFINE_VTABLE_PTR_HELPER_CTOR(UMyClass);    //HotReload相關(guān),先忽略
    UPackage* Z_Construct_UPackage__Script_Hello()  //構(gòu)造Hello的UPackage
    {        static UPackage* ReturnPackage = NULL;        if (!ReturnPackage)
        {
            ReturnPackage = CastChecked<UPackage>(StaticFindObjectFast(UPackage::StaticClass(), NULL, FName(TEXT("/Script/Hello")), false, false));//注意的是這里只是做一個(gè)查找,真正的CreatePackage是在UObjectBase::DeferredRegister里調(diào)用的,后續(xù)在流程里會(huì)討論到
            ReturnPackage->SetPackageFlags(PKG_CompiledIn | 0x00000000);//設(shè)定標(biāo)記和Guid
            FGuid Guid;
            Guid.A = 0x79A097CD;
            Guid.B = 0xB58D8B48;
            Guid.C = 0x00000000;
            Guid.D = 0x00000000;
            ReturnPackage->SetGuid(Guid);

        }        return ReturnPackage;
    }#endifPRAGMA_ENABLE_DEPRECATION_WARNINGS

大部分簡(jiǎn)單的都注釋說明了,本文件的關(guān)鍵點(diǎn)在于IMPLEMENT_CLASS的分析,和上文.h中的DECLARE_CLASS對(duì)應(yīng),其聲明如下:
對(duì)照著定義IMPLEMENT_CLASS(UMyClass, 899540749);

#define IMPLEMENT_CLASS(TClass, TClassCrc) \    static TClassCompiledInDefer<TClass> AutoInitialize##TClass(TEXT(#TClass), sizeof(TClass), TClassCrc); \   //延遲注冊(cè)
    UClass* TClass::GetPrivateStaticClass(const TCHAR* Package) \   //.h里聲明的實(shí)現(xiàn),StaticClas()內(nèi)部就是調(diào)用該函數(shù)
    { \        static UClass* PrivateStaticClass = NULL; \ //又一次static lazy
        if (!PrivateStaticClass) \
        { \            /* this could be handled with templates, but we want it external to avoid code bloat */ \
            GetPrivateStaticClassBody( \    //該函數(shù)就是真正創(chuàng)建UClass*,以后
                Package, \  //Package名字
                (TCHAR*)TEXT(#TClass) + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0), \//類名,+1去掉U、A、F前綴,+11去掉_Deprecated前綴
                PrivateStaticClass, \   //輸出引用
                StaticRegisterNatives##TClass, \                sizeof(TClass), \
                TClass::StaticClassFlags, \
                TClass::StaticClassCastFlags(), \
                TClass::StaticConfigName(), \
                (UClass::ClassConstructorType)InternalConstructor<TClass>, \
                (UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<TClass>, \
                &TClass::AddReferencedObjects, \
                &TClass::Super::StaticClass, \
                &TClass::WithinClass::StaticClass \
            ); \
        } \        return PrivateStaticClass; \
    }

內(nèi)容也比較簡(jiǎn)單,就是把該類的信息傳進(jìn)去給GetPrivateStaticClassBody函數(shù)。

最后展開結(jié)果

通過人肉預(yù)處理展開一下生成的文件,應(yīng)該會(huì)看得更加清楚一些:
MyClass.h展開

#pragma once#include "UObject/NoExportTypes.h"class HELLO_API UMyClass : public UObject
{private:    static void StaticRegisterNativesUMyClass();    friend HELLO_API class UClass* Z_Construct_UClass_UMyClass();private:
    UMyClass& operator=(UMyClass&&);
    UMyClass& operator=(const UMyClass&);    NO_API static UClass* GetPrivateStaticClass(const TCHAR* Package);public:    /** Bitwise union of #EClassFlags pertaining to this class.*/
    enum {StaticClassFlags = CLASS_Intrinsic};    /** Typedef for the base class ({{ typedef-type }}) */
    typedef UObject Super;    /** Typedef for {{ typedef-type }}. */
    typedef UMyClass ThisClass;    /** Returns a UClass object representing this class at runtime */
    inline static UClass* StaticClass()
    {        return GetPrivateStaticClass(TEXT("/Script/Hello"));
    }    /** Returns the StaticClassFlags for this class */
    inline static EClassCastFlags StaticClassCastFlags()
    {        return 0;
    }
    DEPRECATED(4.7, "operator new has been deprecated for UObjects - please use NewObject or NewNamedObject instead")
    inline void* operator new(const size_t InSize, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags)
    {        return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags);
    }    /** For internal use only; use StaticConstructObject() to create new objects. */
    inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags)
    {        return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags);
    }    /** For internal use only; use StaticConstructObject() to create new objects. */
    inline void* operator new(const size_t InSize, EInternal* InMem)
    {        return (void*)InMem;
    }    friend FArchive &operator<<(FArchive& Ar, UMyClass*& Res)
    {        return Ar << (UObject*&)Res;
    }    /** Indicates whether the class is compiled into the engine */
    enum { IsIntrinsic = COMPILED_IN_INTRINSIC };    /** Standard constructor, called after all reflected properties have been initialized */
    NO_API UMyClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { };private:    /** Private move- and copy-constructors, should never be used */
    NO_API UMyClass(UMyClass&&);    NO_API UMyClass(const UMyClass&);public:    static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())UMyClass(X); }
};

Hello.generated.cpp展開

//#include "Hello.h"#include "Engine.h" //#include "GeneratedCppIncludes.h"#include "CoreUObject.h"#include "UObject/Object.h"#include "UObject/Class.h"#include "UObject/Package.h"#include "UObject/MetaData.h"#include "UObject/UnrealType.h"//#include "Hello.generated.dep.h"#include "MyClass.h"void EmptyLinkFunctionForGeneratedCode1Hello() {}void UMyClass::StaticRegisterNativesUMyClass()
{
}static TClassCompiledInDefer<UMyClass> AutoInitializeUMyClass(TEXT("UMyClass"), sizeof(UMyClass), 899540749);
UClass* UMyClass::GetPrivateStaticClass(const TCHAR* Package)
{    static UClass* PrivateStaticClass = NULL;    if (!PrivateStaticClass)
    {        /* this could be handled with templates, but we want it external to avoid code bloat */
        GetPrivateStaticClassBody(
            Package,
            (TCHAR*)TEXT("UMyClass") + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0),
            PrivateStaticClass,
            StaticRegisterNativesUMyClass,            sizeof(UMyClass),
            UMyClass::StaticClassFlags,
            UMyClass::StaticClassCastFlags(),
            UMyClass::StaticConfigName(),
            (UClass::ClassConstructorType)InternalConstructor<UMyClass>,
            (UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<UMyClass>,
            &UMyClass::AddReferencedObjects,
            &UMyClass::Super::StaticClass,
            &UMyClass::WithinClass::StaticClass
        );
    }    return PrivateStaticClass;
}// Cross Module ReferencesCOREUOBJECT_API class UClass* Z_Construct_UClass_UObject();HELLO_API class UClass* Z_Construct_UClass_UMyClass_NoRegister();HELLO_API class UClass* Z_Construct_UClass_UMyClass();HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello();UClass* Z_Construct_UClass_UMyClass_NoRegister(){    return UMyClass::StaticClass();
}UClass* Z_Construct_UClass_UMyClass(){    static UClass* OuterClass = NULL;    if (!OuterClass)
    {
        Z_Construct_UClass_UObject();
        Z_Construct_UPackage__Script_Hello();
        OuterClass = UMyClass::StaticClass();        if (!(OuterClass->ClassFlags & CLASS_Constructed))
        {
            UObjectForceRegistration(OuterClass);
            OuterClass->ClassFlags |= 0x20100080;


            OuterClass->StaticLink();#if WITH_METADATA
            UMetaData* MetaData = OuterClass->GetOutermost()->GetMetaData();
            MetaData->SetValue(OuterClass, TEXT("IncludePath"), TEXT("MyClass.h"));
            MetaData->SetValue(OuterClass, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));#endif
        }
    }
    check(OuterClass->GetClass());    return OuterClass;
}static FCompiledInDefer Z_CompiledInDefer_UClass_UMyClass(Z_Construct_UClass_UMyClass, &UMyClass::StaticClass, TEXT("UMyClass"), false, nullptr, nullptr, nullptr);UPackage* Z_Construct_UPackage__Script_Hello(){    static UPackage* ReturnPackage = NULL;    if (!ReturnPackage)
    {
        ReturnPackage = CastChecked<UPackage>(StaticFindObjectFast(UPackage::StaticClass(), NULL, FName(TEXT("/Script/Hello")), false, false));
        ReturnPackage->SetPackageFlags(PKG_CompiledIn | 0x00000000);
        FGuid Guid;
        Guid.A = 0x79A097CD;
        Guid.B = 0xB58D8B48;
        Guid.C = 0x00000000;
        Guid.D = 0x00000000;
        ReturnPackage->SetGuid(Guid);

    }    return ReturnPackage;
}

這樣.h的聲明和.cpp的定義就全都有了。不管定義了多少函數(shù),要記得注冊(cè)的入口就是那兩個(gè)static對(duì)象在程序啟動(dòng)的時(shí)候登記信息,才有了之后的注冊(cè)。

UENUM的生成代碼剖析

接著是相對(duì)簡(jiǎn)單的Enum,我們測(cè)試的Enum如下:

#pragma once#include "UObject/NoExportTypes.h"#include "MyEnum.generated.h"UENUM(BlueprintType)enum class EMyEnum : uint8
{    MY_Dance    UMETA(DisplayName = "Dance"),
    MY_Rain     UMETA(DisplayName = "Rain"),
    MY_Song     UMETA(DisplayName = "Song")};

生成的MyEnum.generated.h為:

PRAGMA_DISABLE_DEPRECATION_WARNINGS#ifdef HELLO_MyEnum_generated_h#error "MyEnum.generated.h already included, missing '#pragma once' in MyEnum.h"#endif#define HELLO_MyEnum_generated_h#undef CURRENT_FILE_ID#define CURRENT_FILE_ID Hello_Source_Hello_MyEnum_h#define FOREACH_ENUM_EMYENUM(op) \  //定義一個(gè)遍歷枚舉值的宏,只是為了方便使用
    op(EMyEnum::MY_Dance) \
    op(EMyEnum::MY_Rain) \
    op(EMyEnum::MY_Song) 
PRAGMA_ENABLE_DEPRECATION_WARNINGS

因此Enum也非常簡(jiǎn)單,所以發(fā)現(xiàn)生成的其實(shí)也沒有什么重要的信息。同樣的,生成的Hello.genrated.cpp中:

#include "Hello.h"#include "GeneratedCppIncludes.h"#include "Hello.generated.dep.h"PRAGMA_DISABLE_DEPRECATION_WARNINGSvoid EmptyLinkFunctionForGeneratedCode1Hello() {}static class UEnum* EMyEnum_StaticEnum()    //定義一個(gè)獲取UEnum便利函數(shù),會(huì)在延遲注冊(cè)的時(shí)候被用到{    extern HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello();    static class UEnum* Singleton = NULL;    if (!Singleton)
    {        extern HELLO_API class UEnum* Z_Construct_UEnum_Hello_EMyEnum();
        Singleton = GetStaticEnum(Z_Construct_UEnum_Hello_EMyEnum, Z_Construct_UPackage__Script_Hello(), TEXT("EMyEnum"));
    }    return Singleton;
}static FCompiledInDeferEnum Z_CompiledInDeferEnum_UEnum_EMyEnum(EMyEnum_StaticEnum, TEXT("/Script/Hello"), TEXT("EMyEnum"), false, nullptr, nullptr);   //延遲注冊(cè)#if USE_COMPILED_IN_NATIVES
    HELLO_API class UEnum* Z_Construct_UEnum_Hello_EMyEnum();    HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello();    UEnum* Z_Construct_UEnum_Hello_EMyEnum()    //構(gòu)造EMyEnum關(guān)聯(lián)的UEnum*
    {
        UPackage* Outer=Z_Construct_UPackage__Script_Hello();        extern uint32 Get_Z_Construct_UEnum_Hello_EMyEnum_CRC();        static UEnum* ReturnEnum = FindExistingEnumIfHotReloadOrDynamic(Outer, TEXT("EMyEnum"), 0, Get_Z_Construct_UEnum_Hello_EMyEnum_CRC(), false);        if (!ReturnEnum)
        {
            ReturnEnum = new(EC_InternalUseOnlyConstructor, Outer, TEXT("EMyEnum"), RF_Public|RF_Transient|RF_MarkAsNative) UEnum(FObjectInitializer());//直接創(chuàng)建該UEnum對(duì)象
            TArray<TPair<FName, uint8>> EnumNames;//設(shè)置枚舉里的名字和值
            EnumNames.Add(TPairInitializer<FName, uint8>(FName(TEXT("EMyEnum::MY_Dance")), 0));
            EnumNames.Add(TPairInitializer<FName, uint8>(FName(TEXT("EMyEnum::MY_Rain")), 1));
            EnumNames.Add(TPairInitializer<FName, uint8>(FName(TEXT("EMyEnum::MY_Song")), 2));
            EnumNames.Add(TPairInitializer<FName, uint8>(FName(TEXT("EMyEnum::MY_MAX")), 3));   //添加一個(gè)默認(rèn)的MAX字段
            ReturnEnum->SetEnums(EnumNames, UEnum::ECppForm::EnumClass);
            ReturnEnum->CppType = TEXT("EMyEnum");#if WITH_METADATA   //設(shè)置元數(shù)據(jù)
            UMetaData* MetaData = ReturnEnum->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnEnum, TEXT("BlueprintType"), TEXT("true"));
            MetaData->SetValue(ReturnEnum, TEXT("ModuleRelativePath"), TEXT("MyEnum.h"));
            MetaData->SetValue(ReturnEnum, TEXT("MY_Dance.DisplayName"), TEXT("Dance"));
            MetaData->SetValue(ReturnEnum, TEXT("MY_Rain.DisplayName"), TEXT("Rain"));
            MetaData->SetValue(ReturnEnum, TEXT("MY_Song.DisplayName"), TEXT("Song"));#endif
        }        return ReturnEnum;
    }    uint32 Get_Z_Construct_UEnum_Hello_EMyEnum_CRC() { return 2000113000U; }    UPackage* Z_Construct_UPackage__Script_Hello()  //設(shè)置Hello項(xiàng)目的Package屬性
    {
        ...略
    }#endifPRAGMA_ENABLE_DEPRECATION_WARNINGS

觀察發(fā)現(xiàn)EMyEnum_StaticEnum其實(shí)并沒有比Z_Construct_UEnum_Hello_EMyEnum實(shí)現(xiàn)更多的其他的功能。GetStaticEnum目前的實(shí)現(xiàn)內(nèi)部也只是非常簡(jiǎn)單的調(diào)用Z_Construct_UEnum_Hello_EMyEnum返回結(jié)果。所以保留著這個(gè)EMyEnum_StaticEnum或許只是為了和UClass的結(jié)構(gòu)保持一致。

USTRUCT的生成代碼剖析

因?yàn)閁STRUCT標(biāo)記的類內(nèi)部并不能定義函數(shù),因此測(cè)試的Struct如下:

#pragma once#include "UObject/NoExportTypes.h"#include "MyStruct.generated.h"USTRUCT(BlueprintType)struct HELLO_API FMyStruct
{
    GENERATED_USTRUCT_BODY()

    UPROPERTY(BlueprintReadWrite)    float Score;
};

生成的MyStruct.generated.h如下:

PRAGMA_DISABLE_DEPRECATION_WARNINGS#ifdef HELLO_MyStruct_generated_h#error "MyStruct.generated.h already included, missing '#pragma once' in MyStruct.h"#endif#define HELLO_MyStruct_generated_h#define Hello_Source_Hello_MyStruct_h_8_GENERATED_BODY \    friend HELLO_API class UScriptStruct* Z_Construct_UScriptStruct_FMyStruct(); \  //給予全局方法友元權(quán)限
    static class UScriptStruct* StaticStruct(); //靜態(tài)函數(shù)返回UScriptStruct*#undef CURRENT_FILE_ID#define CURRENT_FILE_ID Hello_Source_Hello_MyStruct_hPRAGMA_ENABLE_DEPRECATION_WARNINGS

同理,根據(jù)GENERATED_USTRUCT_BODY的定義,最終會(huì)替換成Hello_Source_Hello_MyStruct_h_8_GENERATED_BODY宏。我們發(fā)現(xiàn)其實(shí)作用只是在內(nèi)部定義了一個(gè)StaticStruct函數(shù),因?yàn)镕MyStruct并不繼承于UObject,所以結(jié)構(gòu)也非常的簡(jiǎn)單。
再接著是Hello.genrated.cpp:

#include "Hello.h"#include "GeneratedCppIncludes.h"#include "Hello.generated.dep.h"PRAGMA_DISABLE_DEPRECATION_WARNINGSvoid EmptyLinkFunctionForGeneratedCode1Hello() {}class UScriptStruct* FMyStruct::StaticStruct()//實(shí)現(xiàn)了靜態(tài)獲取UScriptStruct*{    extern HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello();    static class UScriptStruct* Singleton = NULL;    if (!Singleton)
    {        extern HELLO_API class UScriptStruct* Z_Construct_UScriptStruct_FMyStruct();        extern HELLO_API uint32 Get_Z_Construct_UScriptStruct_FMyStruct_CRC();
        Singleton = GetStaticStruct(Z_Construct_UScriptStruct_FMyStruct, Z_Construct_UPackage__Script_Hello(), TEXT("MyStruct"), sizeof(FMyStruct), Get_Z_Construct_UScriptStruct_FMyStruct_CRC());
    }    return Singleton;
}static FCompiledInDeferStruct Z_CompiledInDeferStruct_UScriptStruct_FMyStruct(FMyStruct::StaticStruct, TEXT("/Script/Hello"), TEXT("MyStruct"), false, nullptr, nullptr);  //延遲注冊(cè)static struct FScriptStruct_Hello_StaticRegisterNativesFMyStruct
{
    FScriptStruct_Hello_StaticRegisterNativesFMyStruct()
    {
        UScriptStruct::DeferCppStructOps(FName(TEXT("MyStruct")),new UScriptStruct::TCppStructOps<FMyStruct>);
    }
} ScriptStruct_Hello_StaticRegisterNativesFMyStruct;    //static注冊(cè)#if USE_COMPILED_IN_NATIVES
    HELLO_API class UScriptStruct* Z_Construct_UScriptStruct_FMyStruct();    HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello();    UScriptStruct* Z_Construct_UScriptStruct_FMyStruct()    //構(gòu)造關(guān)聯(lián)的UScriptStruct*
    {
        UPackage* Outer = Z_Construct_UPackage__Script_Hello();        extern uint32 Get_Z_Construct_UScriptStruct_FMyStruct_CRC();        static UScriptStruct* ReturnStruct = FindExistingStructIfHotReloadOrDynamic(Outer, TEXT("MyStruct"), sizeof(FMyStruct), Get_Z_Construct_UScriptStruct_FMyStruct_CRC(), false);        if (!ReturnStruct)
        {
            ReturnStruct = new(EC_InternalUseOnlyConstructor, Outer, TEXT("MyStruct"), RF_Public|RF_Transient|RF_MarkAsNative) UScriptStruct(FObjectInitializer(), NULL, new UScriptStruct::TCppStructOps<FMyStruct>, EStructFlags(0x00000201));//直接創(chuàng)建UScriptStruct對(duì)象
            UProperty* NewProp_Score = new(EC_InternalUseOnlyConstructor, ReturnStruct, TEXT("Score"), RF_Public|RF_Transient|RF_MarkAsNative) UFloatProperty(CPP_PROPERTY_BASE(Score, FMyStruct), 0x0010000000000004);//直接關(guān)聯(lián)相應(yīng)的Property信息
            ReturnStruct->StaticLink(); //鏈接#if WITH_METADATA   //元數(shù)據(jù)
            UMetaData* MetaData = ReturnStruct->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnStruct, TEXT("BlueprintType"), TEXT("true"));
            MetaData->SetValue(ReturnStruct, TEXT("ModuleRelativePath"), TEXT("MyStruct.h"));
            MetaData->SetValue(NewProp_Score, TEXT("Category"), TEXT("MyStruct"));
            MetaData->SetValue(NewProp_Score, TEXT("ModuleRelativePath"), TEXT("MyStruct.h"));#endif
        }        return ReturnStruct;
    }    uint32 Get_Z_Construct_UScriptStruct_FMyStruct_CRC() { return 2914362188U; }    UPackage* Z_Construct_UPackage__Script_Hello()
    {
        ...略
    }#endifPRAGMA_ENABLE_DEPRECATION_WARNINGS

同理,會(huì)發(fā)現(xiàn)FMyStruct::StaticStruct內(nèi)部也不會(huì)比Z_Construct_UScriptStruct_FMyStruct更多的事情,GetStaticStruct的實(shí)現(xiàn)也只是簡(jiǎn)單的轉(zhuǎn)發(fā)到Z_Construct_UScriptStruct_FMyStruct。值得一提的是定義的ScriptStruct_Hello_StaticRegisterNativesFMyStruct,會(huì)在程序一啟動(dòng)就調(diào)用UScriptStruct::DeferCppStructOps向程序注冊(cè)該結(jié)構(gòu)的CPP信息(大小,內(nèi)存對(duì)齊等),和TClassCompiledInDefer<TClass>的作用相當(dāng)。FMyStruct的展開也是一目了然,就不再贅述了。

UINTERFACE的生成代碼剖析

UE對(duì)Interface也有支持,如果說FStruct就是一個(gè)純數(shù)據(jù)的POD容器,那么UInterface則是一個(gè)只能帶方法的純接口,比C++里的抽象類要根據(jù)的純粹一些。當(dāng)然這里談的都只涉及到用UPROPERTY和UFUNCTION宏標(biāo)記的那些,如果是純C++的字段和函數(shù),UE并不能管到那么寬。
測(cè)試的MyInterface.h為:

#pragma once#include "UObject/NoExportTypes.h"#include "MyInterface.generated.h"UINTERFACE(BlueprintType)class UMyInterface : public UInterface
{
    GENERATED_UINTERFACE_BODY()    
};class IMyInterface
{
    GENERATED_IINTERFACE_BODY()public:
    UFUNCTION(BlueprintImplementableEvent)    void BPFunc() const;
};

GENERATED_UINTERFACE_BODY和GENERATED_IINTERFACE_BODY都可以替換為GENERATED_BODY以提供一個(gè)默認(rèn)的UMyInterface(const FObjectInitializer& ObjectInitializer)構(gòu)造函數(shù)實(shí)現(xiàn)。不過GENERATED_IINTERFACE_BODY替換過后的效果也一樣,因?yàn)椴⒉恍枰敲匆粋€(gè)構(gòu)造函數(shù),所以用兩個(gè)都可以。
生成的MyInterface.generated.h如下:

PRAGMA_DISABLE_DEPRECATION_WARNINGS#ifdef HELLO_MyInterface_generated_h#error "MyInterface.generated.h already included, missing '#pragma once' in MyInterface.h"#endif#define HELLO_MyInterface_generated_h#define Hello_Source_Hello_MyInterface_h_8_RPC_WRAPPERS#define Hello_Source_Hello_MyInterface_h_8_RPC_WRAPPERS_NO_PURE_DECLS#define Hello_Source_Hello_MyInterface_h_8_EVENT_PARMSextern HELLO_API  FName HELLO_BPFunc;   //函數(shù)的名稱,在cpp中定義#define Hello_Source_Hello_MyInterface_h_8_CALLBACK_WRAPPERS#define Hello_Source_Hello_MyInterface_h_8_STANDARD_CONSTRUCTORS \    /** Standard constructor, called after all reflected properties have been initialized */ \    NO_API UMyInterface(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); \    DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyInterface) \    DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, UMyInterface); \DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyInterface); \private: \    /** Private move- and copy-constructors, should never be used */ \    NO_API UMyInterface(UMyInterface&&); \    NO_API UMyInterface(const UMyInterface&); \public:#define Hello_Source_Hello_MyInterface_h_8_ENHANCED_CONSTRUCTORS \    /** Standard constructor, called after all reflected properties have been initialized */ \    NO_API UMyInterface(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { }; \private: \    /** Private move- and copy-constructors, should never be used */ \    NO_API UMyInterface(UMyInterface&&); \    NO_API UMyInterface(const UMyInterface&); \public: \    DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, UMyInterface); \DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyInterface); \    DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyInterface)#undef GENERATED_UINTERFACE_BODY_COMMON#define GENERATED_UINTERFACE_BODY_COMMON() \    private: \    static void StaticRegisterNativesUMyInterface(); \  //注冊(cè)
    friend HELLO_API class UClass* Z_Construct_UClass_UMyInterface(); \ //構(gòu)造UClass*的方法public: \
    DECLARE_CLASS(UMyInterface, UInterface, COMPILED_IN_FLAGS(CLASS_Abstract | CLASS_Interface), 0, TEXT("/Script/Hello"), NO_API) \
    DECLARE_SERIALIZER(UMyInterface) \    enum {IsIntrinsic=COMPILED_IN_INTRINSIC};#define Hello_Source_Hello_MyInterface_h_8_GENERATED_BODY_LEGACY \        PRAGMA_DISABLE_DEPRECATION_WARNINGS \    GENERATED_UINTERFACE_BODY_COMMON() \    Hello_Source_Hello_MyInterface_h_8_STANDARD_CONSTRUCTORS \    PRAGMA_ENABLE_DEPRECATION_WARNINGS#define Hello_Source_Hello_MyInterface_h_8_GENERATED_BODY \    PRAGMA_DISABLE_DEPRECATION_WARNINGS \    GENERATED_UINTERFACE_BODY_COMMON() \    Hello_Source_Hello_MyInterface_h_8_ENHANCED_CONSTRUCTORS \private: \    PRAGMA_ENABLE_DEPRECATION_WARNINGS#define Hello_Source_Hello_MyInterface_h_8_INCLASS_IINTERFACE_NO_PURE_DECLS \protected: \    virtual ~IMyInterface() {} \public: \    typedef UMyInterface UClassType; \    static void Execute_BPFunc(const UObject* O); \    virtual UObject* _getUObject() const = 0;#define Hello_Source_Hello_MyInterface_h_8_INCLASS_IINTERFACE \protected: \    virtual ~IMyInterface() {} \public: \    typedef UMyInterface UClassType; \    static void Execute_BPFunc(const UObject* O); \    virtual UObject* _getUObject() const = 0;#define Hello_Source_Hello_MyInterface_h_5_PROLOG \    Hello_Source_Hello_MyInterface_h_8_EVENT_PARMS#define Hello_Source_Hello_MyInterface_h_13_GENERATED_BODY_LEGACY \PRAGMA_DISABLE_DEPRECATION_WARNINGS \public: \    Hello_Source_Hello_MyInterface_h_8_RPC_WRAPPERS \    Hello_Source_Hello_MyInterface_h_8_CALLBACK_WRAPPERS \    Hello_Source_Hello_MyInterface_h_8_INCLASS_IINTERFACE \public: \PRAGMA_ENABLE_DEPRECATION_WARNINGS#define Hello_Source_Hello_MyInterface_h_13_GENERATED_BODY \PRAGMA_DISABLE_DEPRECATION_WARNINGS \public: \    Hello_Source_Hello_MyInterface_h_8_RPC_WRAPPERS_NO_PURE_DECLS \    Hello_Source_Hello_MyInterface_h_8_CALLBACK_WRAPPERS \    Hello_Source_Hello_MyInterface_h_8_INCLASS_IINTERFACE_NO_PURE_DECLS \private: \PRAGMA_ENABLE_DEPRECATION_WARNINGS#undef CURRENT_FILE_ID#define CURRENT_FILE_ID Hello_Source_Hello_MyInterface_hPRAGMA_ENABLE_DEPRECATION_WARNINGS

因?yàn)榻涌诘亩x需要用到兩個(gè)類,所以生成的信息稍微繁復(fù)了一些。不過使用的時(shí)候,我們的類只是繼承于IMyInterface,UMyInerface只是作為一個(gè)接口類型的載體,用以區(qū)分和查找不同的接口。觀察的時(shí)候,也請(qǐng)注意行號(hào)的定義。
從底往上,最后兩個(gè)是IMyInterface里的宏展開,細(xì)看之后,會(huì)發(fā)現(xiàn)_LEGACY和正常版本并沒有差別。展開后是:

class IMyInterface
{protected: 
    virtual ~IMyInterface() {}  //禁止用接口指針釋放對(duì)象public: 
    typedef UMyInterface UClassType;    //設(shè)定UMyInterface為關(guān)聯(lián)的類型
    static void Execute_BPFunc(const UObject* O);   //藍(lán)圖調(diào)用的輔助函數(shù)
    virtual UObject* _getUObject() const = 0;   //public:
    UFUNCTION(BlueprintImplementableEvent)    void BPFunc() const;
};

再往上是UMyInterface的生成,因?yàn)閁MyInterface繼承于UObject的原因,所以也是從屬于Object系統(tǒng)的一份子,所以同樣需要遵循構(gòu)造函數(shù)的規(guī)則。UInterface本身其實(shí)也可以算是UClass的一種,所以生成的代碼跟UClass中的生成都差不多,區(qū)別是用了COMPILED_IN_FLAGS(CLASS_Abstract | CLASS_Interface)的不同標(biāo)記。有興趣的讀者可以自己展開看下。

生成的Hello.generated.cpp:

#include "Hello.h"#include "GeneratedCppIncludes.h"#include "Hello.generated.dep.h"PRAGMA_DISABLE_DEPRECATION_WARNINGSvoid EmptyLinkFunctionForGeneratedCode1Hello() {}
FName HELLO_BPFunc = FName(TEXT("BPFunc")); //名字的定義
    void IMyInterface::BPFunc() const   //讓編譯通過,同時(shí)加上錯(cuò)誤檢測(cè)
    {
        check(0 && "Do not directly call Event functions in Interfaces. Call Execute_BPFunc instead.");
    }    void UMyInterface::StaticRegisterNativesUMyInterface()
    {
    }
    IMPLEMENT_CLASS(UMyInterface, 4286549343);  //注冊(cè)類
    void IMyInterface::Execute_BPFunc(const UObject* O) //藍(lán)圖調(diào)用方法的實(shí)現(xiàn)
    {
        check(O != NULL);
        check(O->GetClass()->ImplementsInterface(UMyInterface::StaticClass()));//檢查是否實(shí)現(xiàn)了該接口
        UFunction* const Func = O->FindFunction(HELLO_BPFunc);  //通過名字找到方法
        if (Func)
        {            const_cast<UObject*>(O)->ProcessEvent(Func, NULL);  //在該對(duì)象上調(diào)用該方法
        }
    }#if USE_COMPILED_IN_NATIVES
    HELLO_API class UFunction* Z_Construct_UFunction_UMyInterface_BPFunc();    HELLO_API class UClass* Z_Construct_UClass_UMyInterface_NoRegister();    HELLO_API class UClass* Z_Construct_UClass_UMyInterface();    HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello();    UFunction* Z_Construct_UFunction_UMyInterface_BPFunc()//構(gòu)造BPFunc的UFunction
    {
        UObject* Outer=Z_Construct_UClass_UMyInterface();   //得到接口UMyInterface*對(duì)象
        static UFunction* ReturnFunction = NULL;        if (!ReturnFunction)
        {
            ReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT("BPFunc"), RF_Public|RF_Transient|RF_MarkAsNative) UFunction(FObjectInitializer(), NULL, 0x48020800, 65535); //直接構(gòu)造函數(shù)對(duì)象
            ReturnFunction->Bind(); //綁定到函數(shù)指針
            ReturnFunction->StaticLink();   //鏈接#if WITH_METADATA   //元數(shù)據(jù)
            UMetaData* MetaData = ReturnFunction->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnFunction, TEXT("ModuleRelativePath"), TEXT("MyInterface.h"));#endif
        }        return ReturnFunction;
    }    UClass* Z_Construct_UClass_UMyInterface_NoRegister()
    {        return UMyInterface::StaticClass();
    }    UClass* Z_Construct_UClass_UMyInterface()
    {        static UClass* OuterClass = NULL;        if (!OuterClass)
        {
            UInterface::StaticClass();  //確?;怳Interface已經(jīng)元數(shù)據(jù)構(gòu)造完成
            Z_Construct_UPackage__Script_Hello();
            OuterClass = UMyInterface::StaticClass();            if (!(OuterClass->ClassFlags & CLASS_Constructed))
            {
                UObjectForceRegistration(OuterClass);
                OuterClass->ClassFlags |= 0x20004081;//CLASS_Constructed|CLASS_Interface|CLASS_Native|CLASS_Abstract

                OuterClass->LinkChild(Z_Construct_UFunction_UMyInterface_BPFunc());//添加子字段

                OuterClass->AddFunctionToFunctionMapWithOverriddenName(Z_Construct_UFunction_UMyInterface_BPFunc(), "BPFunc"); // 1371259725 ,添加函數(shù)名字映射
                OuterClass->StaticLink();   //鏈接#if WITH_METADATA   //元數(shù)據(jù)
                UMetaData* MetaData = OuterClass->GetOutermost()->GetMetaData();
                MetaData->SetValue(OuterClass, TEXT("BlueprintType"), TEXT("true"));
                MetaData->SetValue(OuterClass, TEXT("ModuleRelativePath"), TEXT("MyInterface.h"));#endif
            }
        }
        check(OuterClass->GetClass());        return OuterClass;
    }    static FCompiledInDefer Z_CompiledInDefer_UClass_UMyInterface(Z_Construct_UClass_UMyInterface, &UMyInterface::StaticClass, TEXT("UMyInterface"), false, nullptr, nullptr, nullptr);    //延遲注冊(cè)
    DEFINE_VTABLE_PTR_HELPER_CTOR(UMyInterface);    UPackage* Z_Construct_UPackage__Script_Hello()
    {
        ...略
    }#endifPRAGMA_ENABLE_DEPRECATION_WARNINGS

基本和UClass中的結(jié)構(gòu)差不多,只是多了一些函數(shù)定義的過程和把函數(shù)添加到類中的操作。

UClass中的字段和函數(shù)生成代碼剖析

在最開始的時(shí)候,我們用了一個(gè)最簡(jiǎn)單的UMyClass來闡述整體的結(jié)構(gòu)。行百里者半九十,讓我們一鼓作氣,看看如果UMyClass里多了Property和Function之后又會(huì)起什么變化。
測(cè)試的MyClass.h如下:

#pragma once#include "UObject/NoExportTypes.h"#include "MyClass.generated.h"UCLASS(BlueprintType)class HELLO_API UMyClass : public UObject
{
    GENERATED_BODY()public:
    UPROPERTY(BlueprintReadWrite)    float Score;public:
    UFUNCTION(BlueprintCallable, Category = "Hello")    void CallableFunc();    //C++實(shí)現(xiàn),藍(lán)圖調(diào)用

    UFUNCTION(BlueprintNativeEvent, Category = "Hello")    void NativeFunc();  //C++實(shí)現(xiàn)默認(rèn)版本,藍(lán)圖可重載實(shí)現(xiàn)

    UFUNCTION(BlueprintImplementableEvent, Category = "Hello")    void ImplementableFunc();   //C++不實(shí)現(xiàn),藍(lán)圖實(shí)現(xiàn)};

增加了一個(gè)屬性和三個(gè)不同方法來測(cè)試。
其生成的MyClass.generated.h為(只包括改變部分):

#define Hello_Source_Hello_MyClass_h_8_RPC_WRAPPERS \    virtual void NativeFunc_Implementation(); \ //默認(rèn)實(shí)現(xiàn)的函數(shù)聲明,我們可以自己實(shí)現(xiàn)
 \
    DECLARE_FUNCTION(execNativeFunc) \  //聲明供藍(lán)圖調(diào)用的函數(shù)
    { \
        P_FINISH; \
        P_NATIVE_BEGIN; \        this->NativeFunc_Implementation(); \
        P_NATIVE_END; \
    } \
 \
    DECLARE_FUNCTION(execCallableFunc) \    //聲明供藍(lán)圖調(diào)用的函數(shù)
    { \
        P_FINISH; \
        P_NATIVE_BEGIN; \        this->CallableFunc(); \
        P_NATIVE_END; \
    }#define Hello_Source_Hello_MyClass_h_8_RPC_WRAPPERS_NO_PURE_DECLS \ //和上面重復(fù),略//聲明函數(shù)名稱extern HELLO_API  FName HELLO_ImplementableFunc;extern HELLO_API  FName HELLO_NativeFunc;

因?yàn)镃allableFunc是C++里實(shí)現(xiàn)的,所以這里并不需要再定義函數(shù)體。而另外兩個(gè)函數(shù)其實(shí)是在藍(lán)圖里定義的,就需要專門生成exec前綴的函數(shù)供藍(lán)圖虛擬機(jī)調(diào)用。
我們展開execCallableFunc后為:

void execCallableFunc( FFrame& Stack, void*const Z_Param__Result )  //藍(lán)圖虛擬機(jī)的使用的函數(shù)接口{
    Stack.Code += !!Stack.Code; /* increment the code ptr unless it is null */
    { 
        FBlueprintEventTimer::FScopedNativeTimer ScopedNativeCallTimer;     //藍(lán)圖的計(jì)時(shí)統(tǒng)計(jì)
        this->CallableFunc(); //調(diào)用我們自己的實(shí)現(xiàn)
    }
}

目前還是非常簡(jiǎn)單的,當(dāng)然根據(jù)函數(shù)簽名的不同會(huì)加上不同的參數(shù)傳遞,但是大體結(jié)構(gòu)就是如此。以上的函數(shù)都是定義在UMyClass類內(nèi)部的。
再來看Hello.generated.cpp里的變化(只包括改變部分):

//函數(shù)名字定義FName HELLO_ImplementableFunc = FName(TEXT("ImplementableFunc"));
FName HELLO_NativeFunc = FName(TEXT("NativeFunc"));    void UMyClass::ImplementableFunc()  //C++端的實(shí)現(xiàn)
    {
        ProcessEvent(FindFunctionChecked(HELLO_ImplementableFunc),NULL);
    }    void UMyClass::NativeFunc() //C++端的實(shí)現(xiàn)
    {
        ProcessEvent(FindFunctionChecked(HELLO_NativeFunc),NULL);
    }    void UMyClass::StaticRegisterNativesUMyClass()  //注冊(cè)函數(shù)名字和函數(shù)指針映射
    {
        FNativeFunctionRegistrar::RegisterFunction(UMyClass::StaticClass(), "CallableFunc",(Native)&UMyClass::execCallableFunc);
        FNativeFunctionRegistrar::RegisterFunction(UMyClass::StaticClass(), "NativeFunc",(Native)&UMyClass::execNativeFunc);
    }//...略去中間相同部分//構(gòu)造3個(gè)函數(shù)的UFunction*對(duì)象,結(jié)構(gòu)一樣,只是EFunctionFlags不一樣UFunction* Z_Construct_UFunction_UMyClass_CallableFunc()
    {
        UObject* Outer=Z_Construct_UClass_UMyClass();        static UFunction* ReturnFunction = NULL;        if (!ReturnFunction)
        {
            ReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT("CallableFunc"), RF_Public|RF_Transient|RF_MarkAsNative) UFunction(FObjectInitializer(), NULL, 0x04020401, 65535); //FUNC_BlueprintCallable|FUNC_Public|FUNC_Native|FUNC_Final
            ReturnFunction->Bind();
            ReturnFunction->StaticLink();#if WITH_METADATA
            UMetaData* MetaData = ReturnFunction->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnFunction, TEXT("Category"), TEXT("Hello"));
            MetaData->SetValue(ReturnFunction, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));#endif
        }        return ReturnFunction;
    }    UFunction* Z_Construct_UFunction_UMyClass_ImplementableFunc()
    {
        UObject* Outer=Z_Construct_UClass_UMyClass();        static UFunction* ReturnFunction = NULL;        if (!ReturnFunction)
        {
            ReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT("ImplementableFunc"), RF_Public|RF_Transient|RF_MarkAsNative) UFunction(FObjectInitializer(), NULL, 0x08020800, 65535); //FUNC_BlueprintEvent|FUNC_Public|FUNC_Event
            ReturnFunction->Bind();
            ReturnFunction->StaticLink();#if WITH_METADATA
            UMetaData* MetaData = ReturnFunction->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnFunction, TEXT("Category"), TEXT("Hello"));
            MetaData->SetValue(ReturnFunction, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));#endif
        }        return ReturnFunction;
    }    UFunction* Z_Construct_UFunction_UMyClass_NativeFunc()
    {
        UObject* Outer=Z_Construct_UClass_UMyClass();        static UFunction* ReturnFunction = NULL;        if (!ReturnFunction)
        {
            ReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT("NativeFunc"), RF_Public|RF_Transient|RF_MarkAsNative) UFunction(FObjectInitializer(), NULL, 0x08020C00, 65535);//FUNC_BlueprintEvent|FUNC_Public|FUNC_Event|FUNC_Native
            ReturnFunction->Bind();
            ReturnFunction->StaticLink();#if WITH_METADATA
            UMetaData* MetaData = ReturnFunction->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnFunction, TEXT("Category"), TEXT("Hello"));
            MetaData->SetValue(ReturnFunction, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));#endif
        }        return ReturnFunction;
    }//...略去中間相同部分UClass* Z_Construct_UClass_UMyClass()
    {        static UClass* OuterClass = NULL;        if (!OuterClass)
        {
            Z_Construct_UClass_UObject();
            Z_Construct_UPackage__Script_Hello();
            OuterClass = UMyClass::StaticClass();            if (!(OuterClass->ClassFlags & CLASS_Constructed))
            {
                UObjectForceRegistration(OuterClass);
                OuterClass->ClassFlags |= 0x20100080;                //添加子字段
                OuterClass->LinkChild(Z_Construct_UFunction_UMyClass_CallableFunc());
                OuterClass->LinkChild(Z_Construct_UFunction_UMyClass_ImplementableFunc());
                OuterClass->LinkChild(Z_Construct_UFunction_UMyClass_NativeFunc());

PRAGMA_DISABLE_DEPRECATION_WARNINGS
                UProperty* NewProp_Score = new(EC_InternalUseOnlyConstructor, OuterClass, TEXT("Score"), RF_Public|RF_Transient|RF_MarkAsNative) UFloatProperty(CPP_PROPERTY_BASE(Score, UMyClass), 0x0010000000000004);//添加屬性PRAGMA_ENABLE_DEPRECATION_WARNINGS                //添加函數(shù)名字映射
                OuterClass->AddFunctionToFunctionMapWithOverriddenName(Z_Construct_UFunction_UMyClass_CallableFunc(), "CallableFunc"); // 774395847
                OuterClass->AddFunctionToFunctionMapWithOverriddenName(Z_Construct_UFunction_UMyClass_ImplementableFunc(), "ImplementableFunc"); // 615168156
                OuterClass->AddFunctionToFunctionMapWithOverriddenName(Z_Construct_UFunction_UMyClass_NativeFunc(), "NativeFunc"); // 3085959641
                OuterClass->StaticLink();#if WITH_METADATA   //元數(shù)據(jù)
                UMetaData* MetaData = OuterClass->GetOutermost()->GetMetaData();
                MetaData->SetValue(OuterClass, TEXT("BlueprintType"), TEXT("true"));
                MetaData->SetValue(OuterClass, TEXT("IncludePath"), TEXT("MyClass.h"));
                MetaData->SetValue(OuterClass, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));
                MetaData->SetValue(NewProp_Score, TEXT("Category"), TEXT("MyClass"));
                MetaData->SetValue(NewProp_Score, TEXT("ModuleRelativePath"), TEXT("MyClass.h"));#endif
            }
        }
        check(OuterClass->GetClass());        return OuterClass;
    }