找回密码
 立即注册
查看: 107|回复: 0

UE4反射系统-前言

[复制链接]
发表于 2023-3-21 11:17 | 显示全部楼层 |阅读模式
引言

学习UE引擎会不可避免地接触到它的反射系统,而这也是UE代码区别于传统C++的一大语言特征。同时,反射系统也是UE用来构建编辑器和蓝图虚拟机的重要基石之一。
目前关于UE反射系统已有很多文章,但笔者在阅读这些文章的过程中,很容易迷失在繁复的语法细节以及茫茫多的宏定义中,忘记原本的主体脉络,最终陷入“看完了,代码也跟着读下来了,没有问题,但好像又什么都没看懂”的迷惑中。
因此本文尝试以人的思维习惯重新梳理UE4反射系统的流程,以运行时反射系统构建的入口代码开始,逐步了解构建过程并反推UHT生成代码的必要性。笔者对于UE也是初学,抱着边学边梳理的想法写下本文,如有错漏之处欢迎指摘。下面进入正题
整体脉络

反射系统的构建离不开下面三个步骤:

  • 编译生成。该步骤依赖于UHT(Unreal Header Tools),在引擎编译过程中,UHT会自动分析代码、为我们生成类型系统需要的中间文件并最终include到对应代码的头文件以及源文件中。
  • 注册&收集。该步骤在运行时main函数执行之前或者新module被加载时执行,借助上一步骤中生成的代码将类信息注册到全局的信息收集表中。
  • 类型系统构建。该步骤在对应模块被加载时执行,模块的加载由引擎的Init或者新module被引用时触发(新module加载会针对该module中的代码重新执行一次步骤2)。
步骤1的中间代码生成细节我们无需深究,因此可以直接从步骤2开始。
在开始看代码之前,我们先自定义一个派生自UObject且不含任何变量和函数的空类,以简化阅读干扰,经过编译后,可以看到代码里自动多出了一些include以及一行GENERATED_BODY的宏
#pragma once

#include "UObject/NoExportTypes.h"
#include "MirrorClass.generated.h"

UCLASS()
class COOPGAME_API UMirrorClass : public UObject
{
        GENERATED_BODY()
};
许多读者应该已经知道,类型系统的运行时触发依赖于C++ Static对象初始化,即Static对象的初始化会在main函数执行之前完成,类型系统正是依赖于这个特性保证足够早地完成构建。
为了避免一开始就陷入宏定义的汪洋大海中,我们这里先不对GENERATED_BODY进行展开分析,只需要先默认这里会关联到UHT自动生成的中间文件即可。这里我们先直接给出反射数据注册的入口,共有两处:
1. TClassCompiledInDefer

该结构是IMPLEMENT_CLASS宏定义中的Static对象
// 代码摘自UE4.18版本, ObjectMacros.h

// Register a class at startup time.
#define IMPLEMENT_CLASS(TClass, TClassCrc) \
        
        // 注: 就是这里!就是这个Static对象
        static TClassCompiledInDefer<TClass> AutoInitialize##TClass(TEXT(#TClass), sizeof(TClass), TClassCrc);// \

        UClass* TClass::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(#TClass) + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0), \
                                PrivateStaticClass, \
                                StaticRegisterNatives##TClass, \
                                sizeof(TClass), \
                                (EClassFlags)TClass::StaticClassFlags, \
                                TClass::StaticClassCastFlags(), \
                                TClass::StaticConfigName(), \
                                (UClass::ClassConstructorType)InternalConstructor<TClass>, \
                                (UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<TClass>, \
                                &TClass::AddReferencedObjects, \
                                &TClass::Super::StaticClass, \
                                &TClass::WithinClass::StaticClass \
                        ); \
                } \
                return PrivateStaticClass; \
        }
IMPLEMENT_CLASS会在UHT生成的类对应中间cpp文件中被调用,例如类名叫MirrorClass,那么生成的对应中间文件分别为MirrorClass.generated.h以及MirrorClass.gen.cpp,而IMPLEMENT_CLASS的调用就在MirrorClass.gen.cpp中。
// 中间文件相对工程目录的存放路径如下
Intermediate\Build\Win64\UE4Editor\Inc\{PROJECT_NAME}
2. FCompiledInDefer

在MirrorClass.gen.cpp中,还定义了另外一个静态Struct,即FCompiledInDefer,一般来说位于该文件的末尾
IMPLEMENT_CLASS(UMirrorClass, 2947269641);

// 这里是另一个Static的Struct
static FCompiledInDefer Z_CompiledInDefer_UClass_UMirrorClass(Z_Construct_UClass_UMirrorClass, &UMirrorClass::StaticClass, TEXT("/Script/CoopGame"), TEXT("UMirrorClass"), false, nullptr, nullptr, nullptr);

DEFINE_VTABLE_PTR_HELPER_CTOR(UMirrorClass);

这两个Static的Struct会在运行时自动构造,其构造函数也就是类型系统的万物起源了。如此一来,就等于找到了迷宫的核心,而我们的目标就是后续弄清楚这两个Static对象的初始化过程到底做了什么以及为什么这么做。
Tips


  • 时刻牢记,反射系统要构建的是一个UClass对象,这个对象用于描述我们的目标类。由于反射系统涉的目的是要构建一个能够描述目标类的对象,这本身就是一个有点绕的话,因此在看代码的时候很容易被绕进去。
  • 相关代码里有很多HotReload相关的逻辑,在刚开始接触的时候可以直接忽略这部分内容,降低理解成本。即直接无视#if WITH_HOT_RELOAD与#endif中间的代码块。

后续文章
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-5-2 15:35 , Processed in 0.106378 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表