Version:4.26.2
UE4 C++工程名:MyProject \
一般語境下,我們說c++源碼的編譯大體分為:預處理、編譯、鏈接; cppreference-translation_phases
虛幻引擎提供了UHT(Unreal Header Tool),在[預處理]之前處理源碼中的各種[UE標記宏]並自動生成輔助代碼;
那么宏GENERATED_BODY()為繼承自UObject的類加了什么?
下面以一個最簡單的類定義來追蹤一下
環境
看下面的簡單類:
file: Source/MyProject/Public/MyProject.h
#include "CoreMinimal.h"
#include "MyObject.generated.h"
UCLASS()
class UMyObject : public UObject
{
GENERATED_BODY();
};
默認配置(Development_Editor, Win64)編譯后,在工程根目錄下打開目錄:Intermediate/Build/Win64/UE4Editor/Inc/MyProject;
在該目錄下可以找到MyObject.gen.cpp, MyObject.generated.h文件,使用VS Code打開這兩個文件備用;
初步的宏展開
UE4涉及到UHT的宏都定義在Engine/Source/Runtime/CoreUObject/Public/UObjectMacros.h源文件中
宏GENERATED_BODY
GENERATED_BODY宏定義在引擎源文件Engine/Source/Runtime/CoreUObject/Public/UObjectMacros.h中,下面是源碼的部分摘錄:
// This pair of macros is used to help implement GENERATED_BODY() and GENERATED_USTRUCT_BODY()
#define BODY_MACRO_COMBINE_INNER(A,B,C,D) A##B##C##D
#define BODY_MACRO_COMBINE(A,B,C,D) BODY_MACRO_COMBINE_INNER(A,B,C,D)
// Include a redundant semicolon at the end of the generated code block, so that intellisense parsers can start parsing
// a new declaration if the line number/generated code is out of date.
#define GENERATED_BODY_LEGACY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY_LEGACY);
#define GENERATED_BODY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY);
#define GENERATED_USTRUCT_BODY(...) GENERATED_BODY()
#define GENERATED_UCLASS_BODY(...) GENERATED_BODY_LEGACY()
#define GENERATED_UINTERFACE_BODY(...) GENERATED_BODY_LEGACY()
#define GENERATED_IINTERFACE_BODY(...) GENERATED_BODY_LEGACY()
初步宏展開會得到:
CURRENT_FILE_ID_15_GENERATED_BODY
頭文件MyProject.h包含了MyProject.generated.h這個頭文件,在MyProject.generated.h中,有如下定義:
#undef CURRENT_FILE_ID
#define CURRENT_FILE_ID MyProject_Source_MyProject_Public_MyObject_h
進一步宏展開得到:
MyProject_Source_MyProject_Public_MyObject_h_15_GENERATED_BODY
查看MyProject.generated.h文件,這個這玩意其實也是一個宏定義:
#define MyProject_Source_MyProject_Public_MyObject_h_15_GENERATED_BODY \
PRAGMA_DISABLE_DEPRECATION_WARNINGS \
public: \
MyProject_Source_MyProject_Public_MyObject_h_15_PRIVATE_PROPERTY_OFFSET \
MyProject_Source_MyProject_Public_MyObject_h_15_SPARSE_DATA \
MyProject_Source_MyProject_Public_MyObject_h_15_RPC_WRAPPERS_NO_PURE_DECLS \
MyProject_Source_MyProject_Public_MyObject_h_15_INCLASS_NO_PURE_DECLS \
MyProject_Source_MyProject_Public_MyObject_h_15_ENHANCED_CONSTRUCTORS \
private: \
PRAGMA_ENABLE_DEPRECATION_WARNINGS
以當前MyObject.h和MyObject.generated.h中的宏定義展開后,會得到下面的UMyObject定義:
class UMyObject : public UObject
{
public:
// MyProject_Source_MyProject_Public_MyObject_h_15_PRIVATE_PROPERTY_OFFSET // 空宏
// MyProject_Source_MyProject_Public_MyObject_h_15_SPARSE_DATA // 空宏
// MyProject_Source_MyProject_Public_MyObject_h_15_RPC_WRAPPERS_NO_PURE_DECLS // 空宏
// ****************** INCLASS_NO_PURE_DECLS 開始 *****************
private:
static void StaticRegisterNativesUMyObject();
friend struct Z_Construct_UClass_UMyObject_Statics;
public:
DECLARE_CLASS(UMyObject, UObject, COMPILED_IN_FLAGS(0), CASTCLASS_None, TEXT("/Script/MyProject"), NO_API)
DECLARE_SERIALIZER(UMyObject)
// ****************** INCLASS_NO_PURE_DECLS 結束 *****************
// ****************** ENHANCED_CONSTRUCTORS 開始 ******************
/** Standard constructor, called after all reflected properties have been initialized */ \
NO_API UMyObject(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { }; \
private: \
/** Private move- and copy-constructors, should never be used */ \
NO_API UMyObject(UMyObject&&); \
NO_API UMyObject(const UMyObject&); \
public: \
DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, UMyObject); \
DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyObject); \
DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyObject)
// ****************** ENHANCED_CONSTRUCTORS 結束 ******************
private:
};
到目前為止可以確定的,GENERATED_BODY宏做了下面這些事:
- 添加了一個靜態函數
static void StaticRegisterNativeUMyObject();
- 聲明結構體
struct Z_Construct_UClass_UMyObject_Statics;
為friend - 添加了一個public構造函數
UMyObject(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
- 通過聲明
private: UMyObject(UMyObject&&); UMyObject(const UMyObject&&);
禁用move和copy
進一步的宏展開
限於篇幅,下面不在給出宏展開后的class UMyObject完整定義代碼
在初步的宏展開后,class UMyObject定義中還有一些在MyObject.h和MyObject.generated.h中沒有找到的宏定義:
- DECLARE_CLASS
- DECLARE_SERIALIZER
- DECLARE_VTABLE_PTR_HELPER_CTOR
- DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER
- DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL
這些宏定義在UE4源代碼Engine/Source/Runtime/CoreUObject/Public/UObject/ObjectMacros.h中
宏DECLARE_CLASS
#define DECLARE_CLASS( TClass, TSuperClass, TStaticFlags, TStaticCastFlags, TPackage, TRequiredAPI ) \
private: \
TClass& operator=(TClass&&); \
TClass& operator=(const TClass&); \
TRequiredAPI static UClass* GetPrivateStaticClass(); \
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(); \
} \
/** Returns the package this class belongs in */ \
inline static const TCHAR* StaticPackage() \
{ \
return TPackage; \
} \
/** Returns the static cast flags for this class */ \
inline static EClassCastFlags StaticClassCastFlags() \
{ \
return TStaticCastFlags; \
} \
/** 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; \
}
可以看到,宏DECLARE_CLASS做了下面這些事:
- 通過聲明
private: UMyObject& operator=(UMyObject&&); UMyObject& operator=(const UMyObject&&);
禁用賦值動作 - 增加一個靜態函數
static UClass* GetPrivateStaticClass();
- 內部枚舉
enum {StaticClassFlags=};
- 內部類型定義Super,表示父類
- 內部類型定義ThisClass,表示該類的UClass對象
- 添加靜態函數
staic UClass* StaticClass();
,用來返回當前類的UClass對象 - 添加靜態函數
static const TCHAR* StaticPackage();
,返回當前類所屬的包名 - 添加靜態函數
inline static EClassCastFlags StaticClassCastFlags()
,放回當前類靜態轉型標記 - 重載operator new函數(如果有機會后面分享到UE4對象的內存管理在來詳細分析)
宏DECLARE_SERIALIZER
#define DECLARE_SERIALIZER( TClass ) \
friend FArchive &operator<<( FArchive& Ar, TClass*& Res ) \
{ \
return Ar << (UObject*&)Res; \
} \
friend void operator<<(FStructuredArchive::FSlot InSlot, TClass*& Res) \
{ \
InSlot << (UObject*&)Res; \
}
該宏為自定義類提供了兩個‘<<’運算符的重載版本
宏DECLARE_VTABLE_PTR_HELPER_CTOR和DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER
#define DECLARE_VTABLE_PTR_HELPER_CTOR(API, TClass) \
/** DO NOT USE. This constructor is for internal usage only for hot-reload purposes. */ \
API TClass(FVTableHelper& Helper);
#if WITH_HOT_RELOAD
#define DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(TClass) \
static UObject* __VTableCtorCaller(FVTableHelper& Helper) \
{ \
return new (EC_InternalUseOnlyConstructor, (UObject*)GetTransientPackage(), NAME_None, RF_NeedLoad | RF_ClassDefaultObject | RF_TagGarbageTemp) TClass(Helper); \
}
#else // WITH_HOT_RELOAD
#define DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(TClass) \
DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER_DUMMY()
#endif // WITH_HOT_RELOAD
這兩個宏為自定義類添加特殊的構造函數聲明和定義,參數為FVTableHelper& Helper
不過通過源碼中的注釋,該構造函數雖然是public的,但僅供引擎內部使用,用於提供熱加載功能;這里打個todo標記,暫時不關注,知道有這么一東西就可以;
宏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 void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass(X); }
注意:該函數體內是一個定位new表達式, UE4 Object的創建會用到;
宏UCLASS
最后是UCLASS宏:
#if UE_BUILD_DOCS || defined(__INTELLISENSE__ )
#define UCLASS(...)
#else
#define UCLASS(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_PROLOG)
#endif
宏UCLASS標記在自定義類定義體的外部;通過上面的宏定義源碼:可以看到它是可以帶參數的;但參數卻沒有傳遞給后面的宏;尤其是第一個條件下的定義就是一個空宏,所以不影響UMyObject類的定義;第二個條件下會拼接成另外一個宏, 但該拼接的宏在當前簡單類中也是一個空宏;所以這里暫時略過;
總結
至此,一個簡單的、加了UCLASS標記的、類定義中加了```GENERATED_BODY()``宏的、繼承自UObject的自定義類的定義就開始清晰了;
也只有分析過后,才了解在Gameplay C++代碼(自定義、引擎源碼)中遇到的:比如Super、StaticClass()等是從哪里來的;
匯總一下,GENERATED_BODY宏給一個簡單的自定類加了下面這些東西:
- 添加了一個靜態函數
static void StaticRegisterNativeUMyObject();
- 聲明結構體
struct Z_Construct_UClass_UMyObject_Statics;
為friend - 添加了一個public構造函數
UMyObject(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
- 通過聲明
private: UMyObject(UMyObject&&); UMyObject(const UMyObject&&);
禁用move和copy - 通過聲明
private: UMyObject& operator=(UMyObject&&); UMyObject& operator=(const UMyObject&&);
禁用賦值動作 - 增加一個靜態函數
static UClass* GetPrivateStaticClass();
- 內部枚舉
enum {StaticClassFlags=};
- 內部類型定義Super,表示父類
- 內部類型定義ThisClass,表示該類的UClass對象
- 添加靜態函數
staic UClass* StaticClass();
,用來返回當前類的UClass對象 - 添加靜態函數
static const TCHAR* StaticPackage();
,返回當前類所屬的包名 - 添加靜態函數
inline static EClassCastFlags StaticClassCastFlags()
,放回當前類靜態轉型標記 - 重載operator new函數(如果有機會后面分享到UE4對象的內存管理在來詳細分析)
除了GENERATED_BODY宏,還有幾個作用類似的宏
- GENERATED_BODY_LEGACY
- GENERATED_UCLASS_BODY
- GENERATED_USTRUCT_BODY
- GENERATED_UINTERFACE_BODY
- GENERATED_IINTERFACE_BODY
它們的作用思路上都是相同的,可以按照同樣的套路分析之;