菜单

《InsideUE4》UObject(四)类型系统代码生成

2018年11月17日 - 注册免费送38元体验金

乃想只要啊?想如果你就是说出嘛,你免说自家怎么亮乃想使吧?

引言

上文说到了UE的门类系统结构,以及UHT分析源码的局部宏标记设定。在都展开了品种系统一体化的计划性后,本文将起来谈论接下的步子。暂时未讨论UHT的细节,假而UHT已经分析得到了足够的类元数据信息,下一致步就是是应用这个消息在程序外存中构建由前文的档次系统结构,这个进程我们誉为注册。同一般程序的构建流程需要通过预处理、编译、汇编、链接一样,UE为了在内存中模拟构建的过程,在概念上吗欲以下几只级次:生成,收集,注册,链接。总体的流水线比较散乱,因此本文首先初步介绍第一品级,生成。在变化等,UHT分析我们的代码,并生成类型系统的系代码。

Note1:生成的代码和登记之过程会以HotReload功能的拉开也有些不平等,因此为了最简化流程阐述,我们事先关HotReload,关闭的点子是于Hello.Build.cs里丰富一行:Definitions.Add(“WITH_HOT_RELOAD_CTORS=0”);
Note2:本文开头跟后续会略的介绍部分运的C++基础知识,但但是点至竣工,不举行深入探讨。

C++ Static Lazy初始化模式

相同种我们常因此,也是UE中常用之单个懒惰初始化模式是:

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

前端非常简单,也没有考虑多线程安全,但是当单线程环境下足用了。用指针的缘故是,有一对景,这些目标的生命周期是出于别的地方来治本的,比如UE里的GC,因此这里就static化一个指南针。否则的话,还是后者尤为从简和安全。

UHT代码生成

以C++程序中之预先处理是用来对源代码进行宏展开,预编译指令处理,注释删除等操作。同样的,一旦我们采取了宏标记之点子,不管是怎个号语法,我们都需开展简单或复杂的词法分析,提取出有因此的信,然后转所待的代码。在发动机里创建一个空C++项目命名吧Hello,然后创建个非连续的MyClass类。编译,UHT就会为我们别以下4独公文(位于Hello\Intermediate\Build\Win64\Hello\Inc\Hello)

其生成的公文初看起很多生复杂,但其实比较简单,不过即使是有些宏替换而已。生成的函数大都也以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.

简单易行,没什么特别意义,就是简单以避免命名冲突,用Z是为字母排序总是出现在智能感知的尽下面,尽量隐藏起来。
连下,请读者们跟我的步伐,开始展开及时趟剖析之同。

UCLASS的变迁代码剖析

预先从一个不过简便易行的UMyClass的始发,总览分析变化的代码结构,接着还接着观察外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”
通过查文件内容,发现是文件于编译的当儿即便是Include了其它部分重新基础之腔文件,比如#include
“Math/Vector.h”,因此若才能够在MyClass里无用include就引述这些近似。当然,还有一对情节是特意供UHT使用来十分成蓝图类型的,现在暂不需要管。

第6行:#include
“MyClass.generated.h”,就是为引用生成的头文件。这里呼吁小心的是,该公文include位置于类似声明的前面,之后说到宏处理的时段会就此到该消息。

第11行:GENERATED_BODY(),该宏是关键,其他的UCLASS宏只是提供信息,不与编译,而GENERATED_BODY正是把声明与处女数据定义关联到齐的关节。继续查看宏定义:

#define BODY_MACRO_COMBINE_INNER(A,B,C,D) A##B##C##D
#define BODY_MACRO_COMBINE(A,B,C,D) BODY_MACRO_COMBINE_INNER(A,B,C,D)
#define GENERATED_BODY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY)

会发现GENERATED_BODY最终骨子里仅仅是转另外一个宏之名,因为:

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

__LINE__标准宏指向了该宏使用时候的的函数,这里是11。加了一个__LINE__宏的目的是为了支持在同一个文件内声明多个类,比如在MyClass.h里接着再声明UMyClass2,就可以支持生成不同的宏名称。

故此总要变化的宏名称是Hello_Source_Hello_MyClass_h_11_GENERATED_BODY,而此宏就是概念在MyClass.generated.h的77执行。值得一提的是,如果MyClass类需要UMyClass(const
FObjectInitializer&
ObjectInitializer)的构造函数自定义实现,则用为此GENERATED_UCLASS_BODY宏来让最终生成的宏指向Hello_Source_Hello_MyClass_h_11_GENERATED_BODY_LEGACY(MyClass.generated.h的66尽),其最后进展的始末会多一个构造函数的内容落实。

MyClass.generated.h

UHT分析生成的文书内容如下:

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 \ //两个重要的定义
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 \    //两个重要的定义
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

该公文都是宏定义,因为宏定义是有前后相继的,因此我们从尾向前看,请读者此时及上文的代码对照着圈。
先是最下是CURRENT_FILE_ID的定义

随后是少数只上文说了的GENERATED_BODY定义,先从不过简单易行的结构开始,不管那些PRIVATE_PROPERTY_OFFSET和PROLOG,以后会逐年介绍至。这半个宏接着包含了4单声明在地方的另特大。目前吧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的特大,如果读者仔细翻看对照的语句,会发觉两者只是差了“:
Super(ObjectInitializer) { }; ”构造函数的默认实现。

我们继续向上,以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) { }; \   //默认的构造函数实现
private: \  //禁止掉C++11的移动和拷贝构造
    /** 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); \     //因为WITH_HOT_RELOAD_CTORS关闭,展开是空宏
    DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyClass); \   //同理,空宏
    DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyClass)

继承翻看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); }

声称定义了一个构造函数包装器。需要如此做的原委是,在根据名字反射创建对象的时刻,需要调用该类的构造函数。可是类的构造函数并无可知为此函数指针指向,因此此就是因故一个static函数包装一下,变成一个”平凡”的函数指针,而且所有类的签约一致,就好当UClass里用一个函数指针里保存起来。见招擎里Class.h的扬言:

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

自然,如果读者欲好实现平等模拟反光框架的早晚啊可采取双重简单之模式,采用模板实现吗是如出一辙。

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

又持续朝上:

#define Hello_Source_Hello_MyClass_h_11_INCLASS \
    private: \
    static void StaticRegisterNativesUMyClass(); \  //定义在cpp中,目前都是空实现
    friend HELLO_API class UClass* Z_Construct_UClass_UMyClass(); \ //一个构造该类UClass对象的辅助函数
    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};   //这个标记指定了该类是C++Native类,不能动态再改变,跟蓝图里构造的动态类进行区分。

可以说DECLARE_CLASS是最为根本之一个声明,对照着定义:DECLARE_CLASS(UMyClass,
UObject, COMPILED_IN_FLAGS(0), 0, TEXT(“/Script/Hello”), NO_API)

大多数都是休开腔自明的,这里的StaticClass就是我们最常使用的函数,其里面调用了GetPrivateStaticClass,而该促成正是以Hello.generated.cpp里之。

Hello.generated.cpp

假若整整Hello项目会转移一个Hello.generated.cpp

#include "Hello.h"      //包含该项目的头文件,继而包含Engine.h
#include "GeneratedCppIncludes.h"   //包含UObject模块里一些必要头文件
#include "Hello.generated.dep.h"    //引用依赖文件,继而include了MyClass.h
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void EmptyLinkFunctionForGeneratedCode1Hello() {}   //先忽略
    void UMyClass::StaticRegisterNativesUMyClass()  //说是静态注册,但现在都是为空,先忽略
    {
    }
    IMPLEMENT_CLASS(UMyClass, 899540749);   //重要!!!
#if USE_COMPILED_IN_NATIVES //该宏编译的时候会打开
// Cross Module References
    COREUOBJECT_API class UClass* Z_Construct_UClass_UObject(); //引用CoreUObject里的函数,主要是为了得到UObject本身对应的UClass

    HELLO_API class UClass* Z_Construct_UClass_UMyClass_NoRegister();   //构造UMyClass对应的UClass对象,区别是没有后续的注册过程
    HELLO_API class UClass* Z_Construct_UClass_UMyClass();  //构造UMyClass对应的UClass对象
    HELLO_API class UPackage* Z_Construct_UPackage__Script_Hello(); //构造Hello本身的UPackage对象
    UClass* Z_Construct_UClass_UMyClass_NoRegister()
    {
        return UMyClass::StaticClass(); //直接通过访问来获取UClass对象
    }
    UClass* Z_Construct_UClass_UMyClass()   //构造并注册
    {
        static UClass* OuterClass = NULL;   //static lazy模式
        if (!OuterClass)
        {
            Z_Construct_UClass_UObject();   //确保UObject本身的UClass已经注册生成
            Z_Construct_UPackage__Script_Hello();   //确保当前Hello项目的UPackage已经创建,因为后续在生成UMyClass的UClass*对象时需要保存在这个UPacage中
            OuterClass = UMyClass::StaticClass();   //访问获得UClass*
            if (!(OuterClass->ClassFlags & CLASS_Constructed))  //防止重复注册
            {
                UObjectForceRegistration(OuterClass);   //提取信息注册自身
                OuterClass->ClassFlags |= 0x20100080;   //增加CLASS_Constructed|CLASS_RequiredAPI标记


                OuterClass->StaticLink();   //“静态”链接,后续解释
#if WITH_METADATA   //编辑器模式下开始
                UMetaData* MetaData = OuterClass->GetOutermost()->GetMetaData();    //获取关联到的UPackage其身上的元数据映射,并增加元数据信息
                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);    //延迟注册,注入信息,在启动的时候调用
    DEFINE_VTABLE_PTR_HELPER_CTOR(UMyClass);    //HotReload相关,先忽略
    UPackage* Z_Construct_UPackage__Script_Hello()  //构造Hello的UPackage
    {
        static UPackage* ReturnPackage = NULL;
        if (!ReturnPackage)
        {
            ReturnPackage = CastChecked<UPackage>(StaticFindObjectFast(UPackage::StaticClass(), NULL, FName(TEXT("/Script/Hello")), false, false));//注意的是这里只是做一个查找,真正的CreatePackage是在UObjectBase::DeferredRegister里调用的,后续在流程里会讨论到
            ReturnPackage->SetPackageFlags(PKG_CompiledIn | 0x00000000);//设定标记和Guid
            FGuid Guid;
            Guid.A = 0x79A097CD;
            Guid.B = 0xB58D8B48;
            Guid.C = 0x00000000;
            Guid.D = 0x00000000;
            ReturnPackage->SetGuid(Guid);

        }
        return ReturnPackage;
    }
#endif

PRAGMA_ENABLE_DEPRECATION_WARNINGS

大部简短的且注释说明了,本文件之重中之重点在于IMPLEMENT_CLASS的分析,和上文.h中的DECLARE_CLASS对应,其声明如下:
对照着定义IMPLEMENT_CLASS(UMyClass, 899540749);

#define IMPLEMENT_CLASS(TClass, TClassCrc) \
    static TClassCompiledInDefer<TClass> AutoInitialize##TClass(TEXT(#TClass), sizeof(TClass), TClassCrc); \   //延迟注册
    UClass* TClass::GetPrivateStaticClass(const TCHAR* Package) \   //.h里声明的实现,StaticClas()内部就是调用该函数
    { \
        static UClass* PrivateStaticClass = NULL; \ //又一次static lazy
        if (!PrivateStaticClass) \
        { \
            /* this could be handled with templates, but we want it external to avoid code bloat */ \
            GetPrivateStaticClassBody( \    //该函数就是真正创建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; \
    }

情节也比较简单,就是拿该类的消息污染进给GetPrivateStaticClassBody函数。

终极进行结果

经人口肉预处理进展一下扭转的文件,应该会扣押得越来越懂得有:
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 References
COREUOBJECT_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的定义就是都有了。不管定义了小函数,要记得注册的输入即是那片个static对象在先后启动的时刻报信息,才来矣后头的注册。

UENUM的转移代码剖析

随之是相对简单的Enum,我们测试的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) \  //定义一个遍历枚举值的宏,只是为了方便使用
    op(EMyEnum::MY_Dance) \
    op(EMyEnum::MY_Rain) \
    op(EMyEnum::MY_Song) 
PRAGMA_ENABLE_DEPRECATION_WARNINGS

因此Enum也非常简单,所以发现变化的实际上呢尚无啊要的信。同样的,生成的Hello.genrated.cpp中:

#include "Hello.h"
#include "GeneratedCppIncludes.h"
#include "Hello.generated.dep.h"
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void EmptyLinkFunctionForGeneratedCode1Hello() {}
static class UEnum* EMyEnum_StaticEnum()    //定义一个获取UEnum便利函数,会在延迟注册的时候被用到
{
    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);   //延迟注册
#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()    //构造EMyEnum关联的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());//直接创建该UEnum对象
            TArray<TPair<FName, uint8>> EnumNames;//设置枚举里的名字和值
            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));   //添加一个默认的MAX字段
            ReturnEnum->SetEnums(EnumNames, UEnum::ECppForm::EnumClass);
            ReturnEnum->CppType = TEXT("EMyEnum");
#if WITH_METADATA   //设置元数据
            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()  //设置Hello项目的Package属性
    {
        ...略
    }
#endif

PRAGMA_ENABLE_DEPRECATION_WARNINGS

考察发现EMyEnum_StaticEnum其实并无比Z_Construct_UEnum_Hello_EMyEnum实现再多的别样的效应。GetStaticEnum目前的兑现中也就是非常简单的调用Z_Construct_UEnum_Hello_EMyEnum返回结果。所以保留在这个EMyEnum_StaticEnum或许只是是为了与UClass的组织保持一致。

USTRUCT的转移代码剖析

以USTRUCT标记的类里并无可知定义函数,因此测试的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(); \  //给予全局方法友元权限
    static class UScriptStruct* StaticStruct(); //静态函数返回UScriptStruct*
#undef CURRENT_FILE_ID
#define CURRENT_FILE_ID Hello_Source_Hello_MyStruct_h
PRAGMA_ENABLE_DEPRECATION_WARNINGS

同理,根据GENERATED_USTRUCT_BODY的概念,最终会交替成Hello_Source_Hello_MyStruct_h_8_GENERATED_BODY宏。我们发现其实作用只是当里边定义了一个StaticStruct函数,因为FMyStruct并无累给UObject,所以组织吧蛮的简单。
又跟着是Hello.genrated.cpp:

#include "Hello.h"
#include "GeneratedCppIncludes.h"
#include "Hello.generated.dep.h"
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void EmptyLinkFunctionForGeneratedCode1Hello() {}
class UScriptStruct* FMyStruct::StaticStruct()//实现了静态获取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);  //延迟注册
static struct FScriptStruct_Hello_StaticRegisterNativesFMyStruct
{
    FScriptStruct_Hello_StaticRegisterNativesFMyStruct()
    {
        UScriptStruct::DeferCppStructOps(FName(TEXT("MyStruct")),new UScriptStruct::TCppStructOps<FMyStruct>);
    }
} ScriptStruct_Hello_StaticRegisterNativesFMyStruct;    //static注册
#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()    //构造关联的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));//直接创建UScriptStruct对象
            UProperty* NewProp_Score = new(EC_InternalUseOnlyConstructor, ReturnStruct, TEXT("Score"), RF_Public|RF_Transient|RF_MarkAsNative) UFloatProperty(CPP_PROPERTY_BASE(Score, FMyStruct), 0x0010000000000004);//直接关联相应的Property信息
            ReturnStruct->StaticLink(); //链接
#if WITH_METADATA   //元数据
            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()
    {
        ...略
    }
#endif

PRAGMA_ENABLE_DEPRECATION_WARNINGS

同理,会意识FMyStruct::StaticStruct内部也未会见比Z_Construct_UScriptStruct_FMyStruct更多之工作,GetStaticStruct的兑现呢只是简单的转化到Z_Construct_UScriptStruct_FMyStruct。值得一提的是概念的ScriptStruct_Hello_StaticRegisterNativesFMyStruct,会以先后一样启动就调用UScriptStruct::DeferCppStructOps向程序报该组织的CPP信息(大小,内存对齐等),和TClassCompiledInDefer<TClass>的打算相当。FMyStruct的拓展也是了如指掌,就不再赘述了。

UINTERFACE的变通代码剖析

UE对Interface也出支撑,如果说FStruct就是一个纯数据的POD容器,那么UInterface则是一个不得不带来道的纯接口,比C++里的纸上谈兵类设因的纯粹一些。当然这里出口的且仅仅提到到用UPROPERTY和UFUNCTION宏标记的那些,如果是纯C++的字段和函数,UE并无可知任及那有钱。
测试的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因提供一个默认的UMyInterface(const
FObjectInitializer&
ObjectInitializer)构造函数实现。不过GENERATED注册免费送38元体验金_IINTERFACE_BODY替换过后的法力呢一律,因为并不需要那么一个构造函数,所以用简单只都可。
生成的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_PARMS
extern HELLO_API  FName HELLO_BPFunc;   //函数的名称,在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(); \  //注册
    friend HELLO_API class UClass* Z_Construct_UClass_UMyInterface(); \ //构造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_h


PRAGMA_ENABLE_DEPRECATION_WARNINGS

为接口的概念需要因此到个别独八九不离十,所以生成的音有点复杂了部分。不过使用的下,我们的近乎才是持续给IMyInterface,UMyInerface只是当做一个接口类型的载体,用以区分和搜索不同的接口。观察的早晚,也求留意行号的定义。
自打之向上,最后两单是IMyInterface里的宏展开,细看之后,会发现_LEGACY和例行版并没差别。展开后是:

class IMyInterface
{
protected: 
    virtual ~IMyInterface() {}  //禁止用接口指针释放对象
public: 
    typedef UMyInterface UClassType;    //设定UMyInterface为关联的类型
    static void Execute_BPFunc(const UObject* O);   //蓝图调用的辅助函数
    virtual UObject* _getUObject() const = 0;   //
public:
    UFUNCTION(BlueprintImplementableEvent)
    void BPFunc() const;
};

更于上是UMyInterface的更动,因为UMyInterface继承于UObject的由来,所以呢是于属于Object系统的一份子,所以一律用遵循构造函数的条条框框。UInterface本身其实呢得算UClass的同种植,所以生成的代码和UClass中之生成都差不多,区别是为此了COMPILED_IN_FLAGS(CLASS_Abstract
| CLASS_Interface)的差标记。有趣味的读者可以协调开展看下。

生成的Hello.generated.cpp:

#include "Hello.h"
#include "GeneratedCppIncludes.h"
#include "Hello.generated.dep.h"
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void EmptyLinkFunctionForGeneratedCode1Hello() {}
FName HELLO_BPFunc = FName(TEXT("BPFunc")); //名字的定义
    void IMyInterface::BPFunc() const   //让编译通过,同时加上错误检测
    {
        check(0 && "Do not directly call Event functions in Interfaces. Call Execute_BPFunc instead.");
    }
    void UMyInterface::StaticRegisterNativesUMyInterface()
    {
    }
    IMPLEMENT_CLASS(UMyInterface, 4286549343);  //注册类
    void IMyInterface::Execute_BPFunc(const UObject* O) //蓝图调用方法的实现
    {
        check(O != NULL);
        check(O->GetClass()->ImplementsInterface(UMyInterface::StaticClass()));//检查是否实现了该接口
        UFunction* const Func = O->FindFunction(HELLO_BPFunc);  //通过名字找到方法
        if (Func)
        {
            const_cast<UObject*>(O)->ProcessEvent(Func, NULL);  //在该对象上调用该方法
        }
    }
#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()//构造BPFunc的UFunction
    {
        UObject* Outer=Z_Construct_UClass_UMyInterface();   //得到接口UMyInterface*对象
        static UFunction* ReturnFunction = NULL;
        if (!ReturnFunction)
        {
            ReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT("BPFunc"), RF_Public|RF_Transient|RF_MarkAsNative) UFunction(FObjectInitializer(), NULL, 0x48020800, 65535); //直接构造函数对象
            ReturnFunction->Bind(); //绑定到函数指针
            ReturnFunction->StaticLink();   //链接
#if WITH_METADATA   //元数据
            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已经元数据构造完成
            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 ,添加函数名字映射
                OuterClass->StaticLink();   //链接
#if WITH_METADATA   //元数据
                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);    //延迟注册
    DEFINE_VTABLE_PTR_HELPER_CTOR(UMyInterface);
    UPackage* Z_Construct_UPackage__Script_Hello()
    {
        ...略
    }
#endif

PRAGMA_ENABLE_DEPRECATION_WARNINGS

主导与UClass中之构造基本上,只是多了有函数定义之长河以及拿函数添加到类似吃的操作。

UClass中之字段和函数生成代码剖析

每当太开头之时光,我们用了一个无限简便易行的UMyClass来论述整体的布局。行百里者半九十,让咱一举,看看如果UMyClass里基本上矣Property和Function之后以见面从什么变化。
测试的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++实现,蓝图调用

    UFUNCTION(BlueprintNativeEvent, Category = "Hello")
    void NativeFunc();  //C++实现默认版本,蓝图可重载实现

    UFUNCTION(BlueprintImplementableEvent, Category = "Hello")
    void ImplementableFunc();   //C++不实现,蓝图实现
};

追加了一个属性和老三只不同方法来测试。
其生成的MyClass.generated.h为(只包反一些):

#define Hello_Source_Hello_MyClass_h_8_RPC_WRAPPERS \
    virtual void NativeFunc_Implementation(); \ //默认实现的函数声明,我们可以自己实现
 \
    DECLARE_FUNCTION(execNativeFunc) \  //声明供蓝图调用的函数
    { \
        P_FINISH; \
        P_NATIVE_BEGIN; \
        this->NativeFunc_Implementation(); \
        P_NATIVE_END; \
    } \
 \
    DECLARE_FUNCTION(execCallableFunc) \    //声明供蓝图调用的函数
    { \
        P_FINISH; \
        P_NATIVE_BEGIN; \
        this->CallableFunc(); \
        P_NATIVE_END; \
    }


#define Hello_Source_Hello_MyClass_h_8_RPC_WRAPPERS_NO_PURE_DECLS \ //和上面重复,略

//声明函数名称
extern HELLO_API  FName HELLO_ImplementableFunc;
extern HELLO_API  FName HELLO_NativeFunc;

为CallableFunc是C++里实现的,所以这边并不需要再定义函数体。而除此以外两单函数其实是在蓝图里定义之,就需特地生成exec前缀的函数供蓝图虚拟机调用。
咱俩展开execCallableFunc后也:

void execCallableFunc( FFrame& Stack, void*const Z_Param__Result )  //蓝图虚拟机的使用的函数接口
{
    Stack.Code += !!Stack.Code; /* increment the code ptr unless it is null */
    { 
        FBlueprintEventTimer::FScopedNativeTimer ScopedNativeCallTimer;     //蓝图的计时统计
        this->CallableFunc(); //调用我们自己的实现
    }
}

手上或非常简单的,当然根据函数签名的异会增长不同的参数传递,但是盖结构就是这么。以上的函数都是概念在UMyClass类内部的。
还来拘禁Hello.generated.cpp里之浮动(只包括改变部分):

//函数名字定义
FName HELLO_ImplementableFunc = FName(TEXT("ImplementableFunc"));
FName HELLO_NativeFunc = FName(TEXT("NativeFunc"));
    void UMyClass::ImplementableFunc()  //C++端的实现
    {
        ProcessEvent(FindFunctionChecked(HELLO_ImplementableFunc),NULL);
    }
    void UMyClass::NativeFunc() //C++端的实现
    {
        ProcessEvent(FindFunctionChecked(HELLO_NativeFunc),NULL);
    }
    void UMyClass::StaticRegisterNativesUMyClass()  //注册函数名字和函数指针映射
    {
        FNativeFunctionRegistrar::RegisterFunction(UMyClass::StaticClass(), "CallableFunc",(Native)&UMyClass::execCallableFunc);
        FNativeFunctionRegistrar::RegisterFunction(UMyClass::StaticClass(), "NativeFunc",(Native)&UMyClass::execNativeFunc);
    }
//...略去中间相同部分
//构造3个函数的UFunction*对象,结构一样,只是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
                //添加函数名字映射
                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   //元数据
                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;
    }

得看到,对于CallableFunc这种C++实现,蓝图只是调用的艺术,生成的代码只是颇成了对应的UFunction*靶。而于NativeFunc和ImplementableFunc,我们无会见以C++里描写上它的实现,因此为了编译通过,也为可以从C++端直接调用,就用以变更的代码的下啊一致很成一客默认实现。
于事先的略近乎生成代码中,StaticRegisterNativesUMyClass总是空的,在此间UHT为它丰富了将函数注册过程序内存的操作。
3独函数的UFunction*变更,虽然其的调用方式大相径庭,但是别的代码的道却是结构同样的,区别之只是不同之EFunctionFlags值。因此好想见出,更多之差距应该是在蓝图虚拟机的有些实现之,该有的文化后介绍蓝图的时候再讨论。
最终,把1单特性和3只点子上加进UClass中,齐活收工。

总结

本篇篇幅较丰富,我们花费了汪洋底讲述阐述UHT生成的代码的体制。首先从一个无比简易的UMyClass开始,观察整变代码的构造,接着推进及UMyEnum、UMyStruct、UMyInterface的代码样式,最后回到归到UMyClass在中添加进属性和方式,观察性与方法是怎么转代码和怎么与UClass*目标关联起来的。其实我们吧发觉,这个等级最要紧的效应就是是拼命三郎的将程序的音之所以代码给记录下来,对于Enum记下名字跟价值;对于Struct记下每个Property的名和字节偏移;对于Interface记下每个函数或包函数的之函数指针和名字;对于Class同理都记录Property和Function。
当,我们现只能干到有尽简单易行的属性与办法类型,目的是叫读者们本着转移的代码来只完整的定义,不要一下子沦为到了复杂的底细被失去。观察生成的代码可知,其实就算分开点儿有些,一凡各种Z_帮扶方法用来布局出各种UClass*对等目标;另一样片段是还饱含在一两个static对象用来在程序启动的时使得登记,继而调用到前者的Z_计,最终完成登记。
在打听及了变化的代码是哪些之后,下篇,我们虽将深入到这些注册的流水线中失去。

题外话

我们呢可以非常轻之观望,UHT生成的及时卖代码并无是绝精简之。比如生成的点滴独特大,最终进行的结果也一如既往,又要坏成了空宏。这一边原因固然是以有诸多之史遗留痕迹,另一方面为是盖以促成UHT的时段,为了照看流程上的合并,并没有改善的失优化掉冗余的子,只是保留了下来。想法是反正UHT生成的代码,不是于开发者读的,所以乱点也不在乎了。最要之凡It
works!所以啊即从未有过什么人发去改善之动力了。
C++的代码生成,一般都未不了要用到大量宏之配合。读者们只要想实现自己的代码生成框架,笔者的提议是不择手段的拿国有的有些挪移到宏定义中失去,并适度的应用模板,尽量最简洁化生成代码的修方式,比如以UHT中之:

FGuid Guid;
Guid.A = 0xDF4B1A6D;
Guid.B = 0x02873257;
Guid.C = 0x00000000;
Guid.D = 0x00000000;
//换成FGuid Guid(0xDF4B1A6D,0x02873257,0x00000000,0x00000000);就会简洁的多。

变迁代码虽然未经常为人念,但是以一些情下补充加脚本绑定,或者好扩大功能,有一个清晰漂亮的代码生成样式,无疑能大大减少理解成本。

引用

UE4.14.3


知乎专栏:InsideUE4
UE4深入学QQ群:456247757(非新手入门群,请先念了官方文档和视频教程)
微信公众号:aboutue,关于UE的尽新闻资讯、技巧问答、文章发布,欢迎关注。
民用原创,未经授权,谢绝转载!

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图