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

这里用来测试的时一个没有方法、没有属性的简单类;

但类型自动注册的总体机制是相同的。

完结

UE4类型数据自动注册的更多相关文章

  1. SpringBoot项目配置Date类型数据自动格式转换

    application.yml加入配置 spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8

  2. Q_DECLARE_METATYPE(继承QObject的类都已经自动注册),注册后的类型可以作为QVariant的自定义类型

    简介 这个宏用来注册一个类(含默认构造.默认析构.拷贝构造函数)为QMetaType类型 ,注册后的类型可以作为QVariant的自定义类型. 这个宏应该放在类或者结构体外面的下面,也可以放在一个非公 ...

  3. 自动注册 IIS6 的 MIME 类型

    原文 自动注册 IIS6 的 MIME 类型 由于IIS5和IIS6有很多的MIME类型没有设置,其中还包括了FLV(video/x-flv),上篇文章描述了制作<IIS6 自动安装>,而 ...

  4. 关于.net 保存 decimal类型数据到SQLServer2012数据库时自动取整的问题

    公司同事问我有没有遇到过decimal类型数据入库时,会自动取整的问题(比如12.3入库后值是12,12.8入库后值是13,入库后自动四舍五入自动取整): 之前就遇到过从数据去decimal类型数据时 ...

  5. 在javaScript中把非数值类型的数据自动转换为数值类型的两种方式

    一.使用Number()函数. 二.使用parseInt()/parseFloat()函数. 详情: 一.使用Number()函数将非数值类型的数据自动的转化为数组类型 Number()函数可以将任何 ...

  6. 6.Struts2简单类型数据的接受

    简单类型数据的接收 在Action类中定义与请求参数同名的属性, 即,要定义该属性的set方法,便能够使struts2自动接收请求参数并赋予同名属性. 简单类型数据的接受举例: 新建工程项目,名称为: ...

  7. jQuery插件:Ajax将Json数据自动绑定到Form表单

    jQuery注册方法的两种常用方式: //jQuery静态方法注册 //调用方法$.a1() $.extend({ a1: function () { console.log("a1&quo ...

  8. 网站集群架构(LVS负载均衡、Nginx代理缓存、Nginx动静分离、Rsync+Inotify全网备份、Zabbix自动注册全网监控)--技术流ken

    前言 最近做了一个不大不小的项目,现就删繁就简单独拿出来web集群这一块写一篇博客.数据库集群请参考<MySQL集群架构篇:MHA+MySQL-PROXY+LVS实现MySQL集群架构高可用/高 ...

  9. Struts2(接受表单参数)请求数据自动封装和数据类型转换

    Struts2请求数据自动封装: (1)实现原理:参数拦截器 (2)方式1:jsp表单数据填充到action中的属性:        普通的成员变量,必须给set,get可以不给的.    注意点,A ...

随机推荐

  1. 数字在排序数组中出现的次数 牛客网 剑指Offer

    数字在排序数组中出现的次数 牛客网 剑指Offer 题目描述 统计一个数字在排序数组中出现的次数. class Solution: def GetNumberOfK(self, data, k): i ...

  2. Luogu P2982 [USACO10FEB]慢下来 Slowing down | dfs序、线段树

    题目链接 题目大意: 有一棵N个结点树和N头奶牛,一开始所有奶牛都在一号结点,奶牛们将按从编号1到编号N的顺序依次前往自己的目的地,求每头奶牛在去往自己目的地的途中将会经过多少已经有奶牛的结点. 题解 ...

  3. IM服务器:开发一个高并发的IM服务器难在哪

    IM服务器要实现的最基本功能就是消息的转发.--好像是一句废话! 这就意味着IM服务器要为每个登录用户创建一个与该用户信息相关的内存上下文,为方便描述我们在这里称之为:user_context.use ...

  4. Go语言实现APPID登录

    package thirdparty import ( "crypto/rsa" "fmt" "github.com/dgrijalva/jwt-go ...

  5. WPF进阶技巧和实战09-事件(1-路由事件、鼠标键盘输入)

    理解路由事件 当有意义的事情发生时,有对象(WPF的元素)发送的用于通知代码的消息,就是事件的核心思想.WPF通过事件路由的概念增强了.NET事件模型.事件由允许源自某个元素的事件由另一个元素引发.例 ...

  6. docker添加sudo权限

    sudo groupadd docker  # 添加group sudo gpasswd -a think docker  # 添加用户到组 sudo service docker restart n ...

  7. Android 有意思的脚本(打印温度)

    https://github.com/LineageOS/android_hardware_google_pixel/blob/lineage-18.1/thermal/device.mk #!/sy ...

  8. 「3dmax」快速配置3DMax

    3DMax学习 1.0.认识3dmax 3dmax(3D Studio Max),版权Autodesk公司持有.是基于PC系统的三维动画渲染和制作软件,主要用于模型构建和骨骼动画制作.行业使用分布广泛 ...

  9. [gym102511K]Traffic Blights

    为了方便,对于集合$S$,称$k\equiv S(mod\ M)$当且仅当存在$x\in S$使得$k\equiv x(mod\ M)$ 枚举红绿灯,对每一个点即限制$k$对$g_{i}+r_{i}$ ...

  10. CKAD认证中的部署教程

    在上一章中,我们已经学会了使用 kubeadm 创建集群和加入新的节点,在本章中,将按照 CKAD 课程的方法重新部署一遍,实际上官方教程的内容不多,笔者写了两篇类似的部署方式,如果已经部署了 kub ...