在解释 UClass 之前,需要先说明 GENERATED_BODY
UCLASS()
class LYRAGAME_API ALyraPawn : public APawn
{
GENERATED_BODY()
private:
UFUNCTION()
void OnControllerChangedTeam(UObject* TeamAgent, int32 OldTeam, int32 NewTeam);
private:
UPROPERTY(ReplicatedUsing = OnRep_MyTeamID)
FGenericTeamId MyTeamID;
UPROPERTY()
FOnLyraTeamIndexChangedDelegate OnTeamChangedDelegate;
private:
UFUNCTION()
void OnRep_MyTeamID(FGenericTeamId OldTeamID);
}
以上述代码为例,在类中使用了 GENERATED_BODY,至于 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_LEGACY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY_LEGACY);
#define GENERATED_BODY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY);
根据上面宏定义代码,可以得到 GENERATED_BODY() 本质就是拼接 CURRENT_FILE_ID + _ + __LINE__ + _GENERATED_BODY
什么是 CURRENT_FILE_ID ?
这段代码需要到对应的 generate.h 文件中去看
#undef CURRENT_FILE_ID
#define CURRENT_FILE_ID FID_Lyra_Source_LyraGame_Character_LyraPawn_h
刚好每个类对应的 generated.h 文件中都定义了各自的 CURRENT_FILE_ID
假设当前 GENERATED_BODY() 被定义在地 20 行,也就是说 __LINE__ 对应的值是 20
最后 GENERATED_BODY() 得到的内容就是 FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_GENERATED_BODY
拿着最后得到的这个字符串,可以在 generated.h 文件中找到对应的宏定义
#define FID_Lyra_Source_LyraGame_Character_LyraPawn_h_17_PROLOG
#define FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_GENERATED_BODY \
PRAGMA_DISABLE_DEPRECATION_WARNINGS \
public: \
FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_SPARSE_DATA \
FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_SPARSE_DATA_PROPERTY_ACCESSORS \
FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_EDITOR_ONLY_SPARSE_DATA_PROPERTY_ACCESSORS \
FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_RPC_WRAPPERS_NO_PURE_DECLS \
FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_ACCESSORS \
FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_INCLASS_NO_PURE_DECLS \
FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_ENHANCED_CONSTRUCTORS \
private: \
PRAGMA_ENABLE_DEPRECATION_WARNINGS
查找 FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_GENERATED_BODY 所包含的全部宏定义
比如 FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_ENHANCED_CONSTRUCTORS 宏
该宏定义了,ALyraPawn 的 移动构造 、拷贝构造、析构虚函数,以及从 FVTableHelper 构造的函数
比如 FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_INCLASS_NO_PURE_DECLS 宏
该宏 使用 DECLARE_SERIALIZER 定义序列化函数,使用了 DECLARE_CLASS 定义了各种工具函数
这里详细看下 DECLARE_CLASS 宏,它定义了许多函数,收集了一些信息
#define DECLARE_CLASS( TClass, TSuperClass, TStaticFlags, TStaticCastFlags, TPackage, TRequiredAPI )
DECLARE_CLASS 传入参数 |
作用 |
|---|---|
| TClass | 当前类名 |
| TSuperClass | 父类名称 |
| TStaticFlags | |
| TStaticCastFlags | |
| TPackage | 所属 Package 名称 |
| TRequiredAPI | 编译宏 |
DECLARE_CLASS 定义了一些函数
DECLARE_CLASS 定义的函数 |
作用 |
|---|---|
| TClass& operator=(TClass&&) | 移动赋值 |
| TClass& operator=(const TClass&); | 拷贝赋值 |
| typedef TSuperClass Super | 定义父类为 Super,这也是为什么可以直接用 Super 调用父类函数的原因 |
| typedef TClass ThisClass; | 定义 ThisClass |
| inline static UClass* StaticClass() | 定义 StaticClass 获取静态数据信息 |
| inline static const TCHAR* StaticPackage() | 获取 Package 信息 |
| inline void* operator new | 创建该类对象的方法,用虚幻提供方式来创建对象,方便内存管理 |
| inline void operator delete | 销毁对象 |
由于 GENERATED_BODY() 定义在类中,宏扩展之后,FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_GENERATED_BODY 扩展出来的所有函数、属性、类中类都会被定义在 ALyraPawn 中
所以可以在编写代码的时候,可以很方便的使用 Super::xxx() 来调用父类函数,也可以使用 T::StaticClass 来获取静态类信息
FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_INCLASS_NO_PURE_DECLS 宏除了定义了 DECLARE_CLASS 和 DECLARE_SERIALIZER 之外,还定义了友元结构体 Z_Construct_UClass_ALyraPawn_Statics
Z_Construct_UClass_ALyraPawn_Statics 的具体实现在对应的 gen.cpp 文件中
以 ALyraPawn::MyTeamID 属性为例
在 Z_Construct_UClass_ALyraPawn_Statics 结构体中定义了与 MyTeamID 关联的属性
#if WITH_METADATA
static const UECodeGen_Private::FMetaDataPairParam NewProp_MyTeamID_MetaData[];
#endif
static const UECodeGen_Private::FStructPropertyParams NewProp_MyTeamID;
它们的初始化也直接定义在 gen.cpp 文件中
#if WITH_METADATA
const UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_ALyraPawn_Statics::NewProp_MyTeamID_MetaData[] = {
{ "ModuleRelativePath", "Character/LyraPawn.h" },
};
#endif
const UECodeGen_Private::FStructPropertyParams Z_Construct_UClass_ALyraPawn_Statics::NewProp_MyTeamID = { "MyTeamID", "OnRep_MyTeamID", (EPropertyFlags)0x0040000100000020, UECodeGen_Private::EPropertyGenFlags::Struct, RF_Public|RF_Transient|RF_MarkAsNative, nullptr, nullptr, 1, STRUCT_OFFSET(ALyraPawn, MyTeamID), Z_Construct_UScriptStruct_FGenericTeamId, METADATA_PARAMS(UE_ARRAY_COUNT(Z_Construct_UClass_ALyraPawn_Statics::NewProp_MyTeamID_MetaData), Z_Construct_UClass_ALyraPawn_Statics::NewProp_MyTeamID_MetaData) }; // 3104706936
以 NewProp_MyTeamID 为例,它的类型是 FStructPropertyParams ,收集了各种信息
struct FStructPropertyParams // : FPropertyParamsBaseWithOffset
{
const char* NameUTF8;
const char* RepNotifyFuncUTF8;
EPropertyFlags PropertyFlags;
EPropertyGenFlags Flags;
EObjectFlags ObjectFlags;
SetterFuncPtr SetterFunc;
GetterFuncPtr GetterFunc;
uint16 ArrayDim;
uint16 Offset;
UScriptStruct* (*ScriptStructFunc)();
#if WITH_METADATA
uint16 NumMetaData;
const FMetaDataPairParam* MetaDataArray;
#endif
};
| FStructPropertyParams 属性 | 作用 | 例子 |
|---|---|---|
| NameUTF8 | 属性名称 | "MyTeamID" |
| RepNotifyFuncUTF8 | 值在服务器上变化时调用的回调函数名称 | "OnRep_MyTeamID" |
| PropertyFlags | 属性枚举,比如 BlueprintCallable 等,可以用于运行时判断或者筛查,具体数值查看 EPropertyFlags 定义 |
(EPropertyFlags)0x0040000100000020 |
| Flags | 数据类型枚举,比如 Byte、Int8、Float 等,具体数值查看 EPropertyGenFlags 枚举 |
UECodeGen_Private::EPropertyGenFlags::Struct |
| ObjectFlags | ||
| SetterFunc | 修改属性时调用函数 | 可以为 nullptr |
| GetterFunc | 获取属性时调用函数 | 可以为 nullptr |
| ArrayDim | ||
| Offset | 属性在类对象上的内存偏移,可以基于内存和属性所占内存大小,直接获取对应内存块 | STRUCT_OFFSET(ALyraPawn, MyTeamID) |
| ScriptStructFunc | ||
| NumMetaData | 属性对应的 MeatData 数组长度 | UE_ARRAY_COUNT(Z_Construct_UClass_ALyraPawn_Statics::NewProp_MyTeamID_MetaData) |
| MetaDataArray | 属性对应的 MetaData 数组 | Z_Construct_UClass_ALyraPawn_Statics::NewProp_MyTeamID_MetaData |
上面这些数据信息都是有 UHT 在编译代码之前收集信息,并生成对应的代码
关于 UClass 常用的两个函数 GetClass 和 T::StaticClass
关于 T::StaticClass 并没有定义在基类中,而是在 generated.h 中
在 generated.h 文件中,有一个名为 DECLARE_CLASS 的宏,这个宏中定义了 StaticClass 函数
inline static UClass* StaticClass() \
{ \
return GetPrivateStaticClass(); \
} \
除了 DECLARE_CLASS 宏中定义了之外,在 generated.h 文件中还直接定义了一个特化模板函数
template<> LYRAGAME_API UClass* StaticClass<class ALyraPawn>();
这个模板函数的实现在 gen.cpp 文件中,本质上还是在调用 DECLARE_CLASS 中定义的 StaticClass
template<> LYRAGAME_API UClass* StaticClass<ALyraPawn>()
{
return ALyraPawn::StaticClass();
}
在 gen.cpp 文件中存在一个与 DECLARE_CLASS 对应的宏 IMPLEMENT_CLASS,不一定是 IMPLEMENT_CLASS 也可能是 IMPLEMENT_CLASS_NO_AUTO_REGISTRATION
该宏实现了 GetPrivateStaticClass 函数,顺便定义了一个 FClassRegistrationInfo 的对象 Z_Registration_Info_UClass_##TClass,以 ALyraPawn 为例,定义的就是 Z_Registration_Info_UClass_ALyraPawn 对象
#define IMPLEMENT_CLASS_NO_AUTO_REGISTRATION(TClass) \
FClassRegistrationInfo Z_Registration_Info_UClass_##TClass; \
UClass* TClass::GetPrivateStaticClass() \
{ \
if (!Z_Registration_Info_UClass_##TClass.InnerSingleton) \
{ \
/* this could be handled with templates, but we want it external to avoid code bloat */ \
GetPrivateStaticClassBody( \
StaticPackage(), \
(TCHAR*)TEXT(#TClass) + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0), \
Z_Registration_Info_UClass_##TClass.InnerSingleton, \
StaticRegisterNatives##TClass, \
sizeof(TClass), \
alignof(TClass), \
TClass::StaticClassFlags, \
TClass::StaticClassCastFlags(), \
TClass::StaticConfigName(), \
(UClass::ClassConstructorType)InternalConstructor<TClass>, \
(UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<TClass>, \
UOBJECT_CPPCLASS_STATICFUNCTIONS_FORCLASS(TClass), \
&TClass::Super::StaticClass, \
&TClass::WithinClass::StaticClass \
); \
} \
return Z_Registration_Info_UClass_##TClass.InnerSingleton; \
}
InternalConstructor 和 InternalVTableHelperCtorCaller 两个函数是全局函数
template<class T>
void InternalConstructor( const FObjectInitializer& X )
{
T::__DefaultConstructor(X);
}
template<class T>
UObject* InternalVTableHelperCtorCaller(FVTableHelper& Helper)
{
return T::__VTableCtorCaller(Helper);
}
而这两个函数已经在 generated.h 中的用于定义构造相关的宏 FID_Lyra_Source_LyraGame_Character_LyraPawn_h_20_ENHANCED_CONSTRUCTORS 中定义
public: \
DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, ALyraPawn); \
DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(ALyraPawn); \
DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(ALyraPawn) \
注意 GetPrivateStaticClassBody 中 InternalConstructor 和 InternalVTableHelperCtorCaller 是函数指针
返回的是类型系统构建时针对 ALyraPawn 类生成的类型对象,它是静态的
GetClass 是对象的内部方法,它返回的是对象的 ClassPrivate 属性,定义在 UObjectBase 中
FORCEINLINE UClass* GetClass() const
{
return ClassPrivate;
}