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::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 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, (UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller, &UMyObject::AddReferencedObjects, &UMyObject::Super::StaticClass, &UMyObject::WithinClass::StaticClass ); } return PrivateStaticClass; } template<> MYPROJECT_API UClass* StaticClass() { 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&) = nullptr) { if (bDynamic) { GetConvertedDynamicPackageNameToTypeName().Add(FName(DynamicPackageName), FName(Name)); } UObjectCompiledInDefer(InRegister, InStaticClass, Name, PackageName, bDynamic, DynamicPathName, InInitSearchableValues); } };

构造函数内部直接调用了UObjectCompiledInDefer(...)函数;
由于参数bDynamic=false,函数UObjectCompiledInDefer(...)内部有效的代码如下:
TArray& DeferredCompiledInRegistration = GetDeferredCompiledInRegistration(); checkSlow(!DeferredCompiledInRegistration.Contains(InRegister)); DeferredCompiledInRegistration.Add(InRegister);

函数GetDeferredCompiledInRegistration()定义如下:
static TArray& GetDeferredCompiledInRegistration() { static TArray DeferredCompiledInRegistration; return DeferredCompiledInRegistration; }

内部的数组DeferredCompiledInRegistration也是静态对象;
参数InRegister这个函数指针加入DeferredCompiledInRegistration数组中;
即函数Z_Construct_UClass_UMyObject地址会被加入到DeferredCompiledInRegistration数组中;
TClassCompiledInDefer类: 【关注点-2】这个类同样定义在UObjectBase.h文件中;
与FCompiledInDefer写法类似
区别是这里将static TClassCompiledInDefer 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 AutoInitializeUMyObject对象,收集名字、size、一个唯一ID;
  • 通过static FCompiledInDefer Z_CompiledInDefer_UClass_UMyObject对象,注册一个函数,该函数返回一个UClass对象,该对象用来描述UMyObject类;
需要重复指出的是,整个类型信息注册的时机是在程序加载阶段,要早于main函数;
这里用来测试的时一个没有方法、没有属性的简单类;
但类型自动注册的总体机制是相同的。
【UE4类型数据自动注册】完结

    推荐阅读