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函數;

這裡用來測試的時一個沒有方法、沒有屬性的簡單類;

但類型自動註冊的總體機制是相同的。

完結

Tags: