UE4類型數據自動註冊
Version:4.26.2
UE4 C++工程名:MyProject
在《宏GENERATED_BODY做了什麼?》中,簡單分析了GENERATED_BODY宏給一個簡單的、繼承自UObject的自定義類添加了什麼。
當中涉及到的源碼文件有:ObjectMacros.h、MyObject.h、MyObject.generated.h, UObjectGlobals.h;
現在來分析一下UHT生成的另外一個文件:MyObject.gen.cpp
當中會額外涉及到的源文件有:UObjectBase.h, UObjectBase.cpp, Class.h, Class.cpp, UObjectGlobals.cpp
MyObject.gen.cpp中有什麼?
先無腦貼一下程式碼,部分會添加中文注釋
// Copyright Epic Games, Inc. All Rights Reserved.
/*===========================================================================
Generated code exported from UnrealHeaderTool.
DO NOT modify this manually! Edit the corresponding .h files instead!
===========================================================================*/
#include "UObject/GeneratedCppIncludes.h"
#include "MyProject/Public/MyObject.h"
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable : 4883)
#endif
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void EmptyLinkFunctionForGeneratedCodeMyObject() {}
// Cross Module References
MYPROJECT_API UClass* Z_Construct_UClass_UMyObject_NoRegister();
MYPROJECT_API UClass* Z_Construct_UClass_UMyObject();
COREUOBJECT_API UClass* Z_Construct_UClass_UObject();
UPackage* Z_Construct_UPackage__Script_MyProject();
// End Cross Module References
void UMyObject::StaticRegisterNativesUMyObject()
{
}
UClass* Z_Construct_UClass_UMyObject_NoRegister()
{
return UMyObject::StaticClass();
}
struct Z_Construct_UClass_UMyObject_Statics
{
static UObject* (*const DependentSingletons[])();
#if WITH_METADATA
static const UE4CodeGen_Private::FMetaDataPairParam Class_MetaDataParams[];
#endif
static const FCppClassTypeInfoStatic StaticCppClassTypeInfo;
static const UE4CodeGen_Private::FClassParams ClassParams;
};
UObject* (*const Z_Construct_UClass_UMyObject_Statics::DependentSingletons[])() = {
(UObject* (*)())Z_Construct_UClass_UObject,
(UObject* (*)())Z_Construct_UPackage__Script_MyProject,
};
#if WITH_METADATA
const UE4CodeGen_Private::FMetaDataPairParam Z_Construct_UClass_UMyObject_Statics::Class_MetaDataParams[] = {
{ "BlueprintType", "true" },
{ "Comment", "/**\n *n */" },
{ "IncludePath", "MyObject.h" },
{ "ModuleRelativePath", "Public/MyObject.h" },
{ "ObjectInitializerConstructorDeclared", "" },
};
#endif
const FCppClassTypeInfoStatic Z_Construct_UClass_UMyObject_Statics::StaticCppClassTypeInfo = {
TCppClassTypeTraits<UMyObject>::IsAbstract,
};
// UE_ARRAY_COUNT宏計算數組的大小
// METADATA_PARAMS簡單的宏拆成兩個參數,或者支取第二個宏;根據WITH_METADATA宏是否有定義;參看UObjectGlobals.h源文件中的定義
const UE4CodeGen_Private::FClassParams Z_Construct_UClass_UMyObject_Statics::ClassParams = {
&UMyObject::StaticClass,
nullptr,
&StaticCppClassTypeInfo,
DependentSingletons,
nullptr,
nullptr,
nullptr,
UE_ARRAY_COUNT(DependentSingletons),
0,
0,
0,
0x001000A0u,
METADATA_PARAMS(Z_Construct_UClass_UMyObject_Statics::Class_MetaDataParams, UE_ARRAY_COUNT(Z_Construct_UClass_UMyObject_Statics::Class_MetaDataParams))
};
// 關注點-1
UClass* Z_Construct_UClass_UMyObject()
{
static UClass* OuterClass = nullptr;
if (!OuterClass)
{
UE4CodeGen_Private::ConstructUClass(OuterClass, Z_Construct_UClass_UMyObject_Statics::ClassParams);
}
return OuterClass;
}
// IMPLEMENT_CLASS(UMyObject, 1944586990); // 下面時宏展開後的程式碼
// 關注點-2
static TClassCompiledInDefer<UMyObject> AutoInitializeUMyObject(TEXT("UMyObject"), sizeof(UMyObject), 1944586990);
UClass* UMyObject::GetPrivateStaticClass()
{
static UClass* PrivateStaticClass = NULL;
if (!PrivateStaticClass)
{
/* this could be handled with templates, but we want it external to avoid code bloat */
GetPrivateStaticClassBody(
StaticPackage(),
(TCHAR*)TEXT("UMyObject") + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0),
PrivateStaticClass,
StaticRegisterNativesUMyObject,
sizeof(UMyObject),
alignof(UMyObject),
(EClassFlags)UMyObject::StaticClassFlags,
UMyObject::StaticClassCastFlags(),
UMyObject::StaticConfigName(),
(UClass::ClassConstructorType)InternalConstructor<UMyObject>,
(UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<UMyObject>,
&UMyObject::AddReferencedObjects,
&UMyObject::Super::StaticClass,
&UMyObject::WithinClass::StaticClass
);
}
return PrivateStaticClass;
}
template<> MYPROJECT_API UClass* StaticClass<UMyObject>()
{
return UMyObject::StaticClass();
}
// 關注點-3
static FCompiledInDefer Z_CompiledInDefer_UClass_UMyObject(Z_Construct_UClass_UMyObject, &UMyObject::StaticClass, TEXT("/Script/MyProject"), TEXT("UMyObject"), false, nullptr, nullptr, nullptr);
// DEFINE_VTABLE_PTR_HELPER_CTOR(UMyObject); // 下面時宏展開後的程式碼
UMyObject::UMyObject(FVTableHelper& Helper) : Super(Helper) {};
PRAGMA_ENABLE_DEPRECATION_WARNINGS
#ifdef _MSC_VER
#pragma warning (pop)
#endif
上面程式碼中加了三個關注點注釋,下面是一點分析
分析
上面羅列了一下MyObject.gen.cpp文件的內容,下面來分析一下關注點
FCompiledInDefer類
先看【關注點-3】吧,該類定義在UObjectBase.h文件中:
struct FCompiledInDefer
{
FCompiledInDefer(class UClass *(*InRegister)(), class UClass *(*InStaticClass)(), const TCHAR* PackageName, const TCHAR* Name, bool bDynamic, const TCHAR* DynamicPackageName = nullptr, const TCHAR* DynamicPathName = nullptr, void (*InInitSearchableValues)(TMap<FName, FName>&) = nullptr)
{
if (bDynamic)
{
GetConvertedDynamicPackageNameToTypeName().Add(FName(DynamicPackageName), FName(Name));
}
UObjectCompiledInDefer(InRegister, InStaticClass, Name, PackageName, bDynamic, DynamicPathName, InInitSearchableValues);
}
};
構造函數內部直接調用了UObjectCompiledInDefer(...)
函數;
由於參數bDynamic=false
,函數UObjectCompiledInDefer(...)
內部有效的程式碼如下:
TArray<UClass *(*)()>& DeferredCompiledInRegistration = GetDeferredCompiledInRegistration();
checkSlow(!DeferredCompiledInRegistration.Contains(InRegister));
DeferredCompiledInRegistration.Add(InRegister);
函數GetDeferredCompiledInRegistration()
定義如下:
static TArray<class UClass *(*)()>& GetDeferredCompiledInRegistration()
{
static TArray<class UClass *(*)()> DeferredCompiledInRegistration;
return DeferredCompiledInRegistration;
}
內部的數組DeferredCompiledInRegistration
也是靜態對象;
參數InRegister
這個函數指針加入DeferredCompiledInRegistration
數組中;
即函數Z_Construct_UClass_UMyObject
地址會被加入到DeferredCompiledInRegistration
數組中;
TClassCompiledInDefer類:
【關注點-2】這個類同樣定義在UObjectBase.h文件中;
與FCompiledInDefer寫法類似
區別是這裡將static TClassCompiledInDefer<UMyObject> AutoInitializeUMyObject
這個對象加入另了一個靜態數組中;
這裡不再贅述了;
函數UClass* Z_Construct_UClass_UMyObject()
【關注點-1】有啥好說的呢?
上面分析,這個函數通過函數指針被添加到靜態數組DeferredCompiledInRegistration
;
這裡需要關注:
- 它的返回值,是一個UClass對象;
- 內部
ConstructUClass
函數調的第二個參數是Z_Construct_UClass_UMyObject_Statics::ClassParams
, 這正是上半部分程式碼對UMyObject類資訊的收集數據
ConstructUClass
這個函數定義在UObjectGlobals.h/UObjectGlobals.cpp文件中;
分析結果
- 結合前面《宏GENERATED_BODY做了什麼?》裡面的分析,這裡會得到一個調用堆棧:
Z_Construct_UClass_UMyObject(…) -> ConstructUClass(…) -> UMyObject::StaticClass() -> UMyObject::GetPrivateStaticClass() -> GetPrivateStaticClassBody(…)
- 並且從
GetPrivateStaticClass()
中就可以看出,一個自定義類只會創建一個UClass對象; - 函數
Z_Construct_UClass_UMyObject
只是做了註冊,真正調用的時候是在引擎初始化階段;
總結:
上面羅列了一下MyObject.gen.cpp中有什麼東西;
並且分下了一下關鍵的註冊機制;
在MyObject.gen.cpp中,通過靜態對象的特性(程式載入時就會被創建,並且時間早於main函數),收集自定義類的資訊:
- 通過
static TClassCompiledInDefer<UMyObject> AutoInitializeUMyObject
對象,收集名字、size、一個唯一ID; - 通過
static FCompiledInDefer Z_CompiledInDefer_UClass_UMyObject
對象,註冊一個函數,該函數返回一個UClass對象,該對象用來描述UMyObject類;
需要重複指出的是,整個類型資訊註冊的時機是在程式載入階段,要早於main函數;
這裡用來測試的時一個沒有方法、沒有屬性的簡單類;
但類型自動註冊的總體機制是相同的。
完結