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. Spring中自定义Schema扩展机制

    一.前言 Spring 为基于 XML 构建的应用提供了一种扩展机制,用于定义和配置 Bean. 它允许使用者编写自定义的 XML bean 解析器,并将解析器本身以及最终定义的 Bean 集成到 S ...

  2. hdu 1198 Farm Irrigation(并查集)

    题意: Benny has a spacious farm land to irrigate. The farm land is a rectangle, and is divided into a ...

  3. hdu 3863 No Gambling (不会证明,但是是对的,,)

    题意: N=4时 规则: 双方每次可以连接自己颜色的两个点(相邻,长度为1),线和线不能交叉重叠. 蓝方要连接左右,红方要连接上下. 蓝方先.问谁先连接? 思路: 经过观察....蓝方胜....... ...

  4. tcp 三次握手建立连接难点总结

    所谓三次握手(Three-way Handshake),是指建立一个TCP连接时,需要客户端和服务器总共发送3个包. 三次握手的目的是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号 ...

  5. Typora软件的使用教程

    一.Typora软件介绍 Typora是一款轻便简洁的Markdown编辑器,支持即时渲染技术,这也是与其他Markdown编辑器最显著的区别.即时渲染使得你写Markdown就想是写Word文档一样 ...

  6. 聊一聊声明式接口调用与Nacos的结合使用

    背景 对于公司内部的 API 接口,在引入注册中心之后,免不了会用上服务发现这个东西. 现在比较流行的接口调用方式应该是基于声明式接口的调用,它使得开发变得更加简化和快捷. .NET 在声明式接口调用 ...

  7. MyCat读写分离+MySql主从(一主一从)

    数据库一直在项目担当着一位核心的角色,是所有项目结构中的底层,说白了,我们程序员进行项目开发都是在和数据打交道,而数据都是保存在数据库中,如mysql.oracle.postgresql等等,如果一个 ...

  8. MAC电脑如何将常规视频中音频提取出来(转换格式并调整采样频率),并利用讯飞语音识别文字

    1.下载好相关视频 2.选中需要提取视频,鼠标右键找到「编码所选视频文件」 3.设置中,下拉选择「仅音频」,点击继续 4.找到已提取成功的音频,鼠标右键或快捷键「command + I」,显示简介.默 ...

  9. Sqlserver中判断表是否存在

    在sqlserver(应该说在目前所有数据库产品)中创建一个资源如表,视图,存储过程中都要判断与创建的资源是否已经存在  在sqlserver中一般可通过查询sys.objects系统表来得知结果,不 ...

  10. Go iota 原理和源码剖析

    iota 是 Go 语言的一个保留字,用作常量计数器.由于 iota 具有自增特性,所以可以简化数字增长的常量定义. iota 是一个具有魔法的关键字,往往令初学者难以理解其原理和使用方法. 本文会从 ...