宏GENERATED_BODY做了什么()

Version:4.26.2
UE4 C++工程名:MyProject \
一般语境下,我们说c++源码的编译大体分为:预处理、编译、链接; cppreference-translation_phases
虚幻引擎提供了UHT(Unreal Header Tool),在预处理之前来先处理源码中的各种宏标记并自动生成辅助代码;
那么宏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做了什么()】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宏做了下面这些事:
  1. 添加了一个静态函数static void StaticRegisterNativeUMyObject();
  2. 声明结构体struct Z_Construct_UClass_UMyObject_Statics; 为friend
  3. 添加了一个public构造函数UMyObject(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
  4. 通过声明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做了下面这些事:
  1. 通过声明private: UMyObject& operator=(UMyObject&&); UMyObject& operator=(const UMyObject&&); 禁用赋值动作
  2. 增加一个静态函数static UClass* GetPrivateStaticClass();
  3. 内部枚举enum {StaticClassFlags=};
  4. 内部类型定义Super,表示父类
  5. 内部类型定义ThisClass,表示该类的UClass对象
  6. 添加静态函数staic UClass* StaticClass(); ,用来返回当前类的UClass对象
  7. 添加静态函数static const TCHAR* StaticPackage(); ,返回当前类所属的包名
  8. 添加静态函数inline static EClassCastFlags StaticClassCastFlags(),放回当前类静态转型标记
  9. 重载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宏给一个简单的自定类加了下面这些东西:
  1. 添加了一个静态函数static void StaticRegisterNativeUMyObject();
  2. 声明结构体struct Z_Construct_UClass_UMyObject_Statics; 为friend
  3. 添加了一个public构造函数UMyObject(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
  4. 通过声明private: UMyObject(UMyObject&&); UMyObject(const UMyObject&&); 禁用move和copy
  5. 通过声明private: UMyObject& operator=(UMyObject&&); UMyObject& operator=(const UMyObject&&); 禁用赋值动作
  6. 增加一个静态函数static UClass* GetPrivateStaticClass();
  7. 内部枚举enum {StaticClassFlags=};
  8. 内部类型定义Super,表示父类
  9. 内部类型定义ThisClass,表示该类的UClass对象
  10. 添加静态函数staic UClass* StaticClass(); ,用来返回当前类的UClass对象
  11. 添加静态函数static const TCHAR* StaticPackage(); ,返回当前类所属的包名
  12. 添加静态函数inline static EClassCastFlags StaticClassCastFlags(),放回当前类静态转型标记
  13. 重载operator new函数(如果有机会后面分享到UE4对象的内存管理在来详细分析)

    推荐阅读