UE5引擎源码小记 —反射信息注册过程

最近看了看反射相关的知识,用不说一点人话的方式来说,反射是程序在运行中能够动态获取修改或调用自身属性的东西。

一开始我是觉得反射用处好像不大,后续查了下一些反射的使用环境,发现我格局小了,我觉得用处不大的原因是我从c++的角度来考虑使用反射,在c++中确实用处不大,但是捏,把格局打开,要从更上一层的视角来看反射,不要局限于c++,比如我们要使用c++以外的东西调用c++,比如配置文件,蓝图,python,lua这些去调c++,或是设计到编辑器功能的时候,这时候反射就是极为好用方便的一个特性。

三种反射实现的方式

反射实现的本质其实就是想办法在运行时拿到类的数据,这个数据可以手动生成也可以利用编译器的信息,在c++中反射大概有三种实现方式

1.利用c++手动写反射结构的代码,可以利用宏之类的,其实就是方式3的简化版,实现简单相对简单,但是对使用者这来说毕竟麻烦,因为如果要添加一个新的成员或函数,就要手动添加一次。

2.利用c++编译出来的符号表,缺点每个编译器的实现不一定一样,换一个编译器可能就不好使了

3.利用工具生成c++代码实现反射,唯一缺点是实现起来比较有难度,但是使用起来简单,也不会有第二种方法的缺点

在ue中使用的就是第三种办法,所以ue实现了UHT这个工具。

UE5中反射系统注册信息的过程

UHT生成xxx.generated.h和xxx.gen.cpp反射信息文件 利用static变量的初始化预注册 注册完成的反射信息

UHT与宏

在编写ue代码时,相比大家一定遇到过类似于UCLASS,GENERATED_BODY,UFUNCTION这些宏,这些宏其实就是给UHT的一个标记,UHT更具这些宏生成generated.h和gen.cpp文件,这些宏在ObjectMarcos.h文件中可以找到详细的定义。现在我们来一步一步拨开这些宏背后所做的事情

比如,现在我们有这样一个AGhost类

cpp 复制代码
UCLASS(config = Ghost)
class LEARNUE_API AGhost : public AActor, public FTickableGameObject
{
	GENERATED_BODY()
public:
	virtual void BeginPlay() override;
	virtual void Tick(float DeltaTime) override;
	AGhost();
	virtual  ~AGhost() override;

	UFUNCTION(BlueprintCallable)
	void PtLog(int i);

public:
	UPROPERTY(VisibleAnywhere)
	int32 m_time ;
};

UCLASS

我们首先来看UCLASS宏的定义

cpp 复制代码
#if UE_BUILD_DOCS || defined(__INTELLISENSE__ )
#define UCLASS(...)
#else
#define UCLASS(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_PROLOG)
#endif
	
#define BODY_MACRO_COMBINE_INNER(A,B,C,D) A##B##C##D
#define BODY_MACRO_COMBINE(A,B,C,D) BODY_MACRO_COMBINE_INNER(A,B,C,D)

可以发现UCLASS这个宏仅仅是将三个字符串拼接起来。CURRENT_FILE_ID代表一个唯一的文件id,LINE为当前行号。

最后这个UCLASS会被替换成空行,我猜想是UCLASS宏内的参数在UHT运行的过程中被替换成flag之类的东西,所以这后续编译时候就没用了,

接着我们来看看UHT生成genrated.h文件可以发现这样的一个宏

cpp 复制代码
#undef CURRENT_FILE_ID
#define CURRENT_FILE_ID FID_UeProject_learnUe_Source_learnUe_Ghost_h
	
#define FID_UeProject_learnUe_Source_learnUe_Ghost_h_13_PROLOG

发现确实在最后UCLASS这个宏会被替换成空行。

GENERATED_BODY

GENERATED_BODY宏和UCLASS宏类似,也是拼接事先定义好内容,然后进行宏替换,不过GENERATED_BODY宏有着实际的替换内容。

cpp 复制代码
#define BODY_MACRO_COMBINE_INNER(A,B,C,D) A##B##C##D
#define BODY_MACRO_COMBINE(A,B,C,D) BODY_MACRO_COMBINE_INNER(A,B,C,D)

#define GENERATED_BODY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY);

然后GENERATED_BODY宏实际定义的东西在generated.h文件中

cpp 复制代码
#define FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_GENERATED_BODY \
PRAGMA_DISABLE_DEPRECATION_WARNINGS \
public: \
	FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_SPARSE_DATA \
	FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_RPC_WRAPPERS_NO_PURE_DECLS \
	FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_ACCESSORS \
	FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_INCLASS_NO_PURE_DECLS \
	FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_ENHANCED_CONSTRUCTORS \
private: \

发现是宏套宏的套娃,不过被急,还有好多层套者呢。如果不想细看的朋友只要知道这些东西就是UHT帮助生成的代码。

SPARSE_DATA为稀疏数据的定义,WRAPPERS_NO_PURE_DECLS为UFunction的定义,ACCESSORS这个目前不太确定是啥,

INCLASS_NO_PURE_DECLS为序列化,StaticClass函数,Flag等一些的定义,DECLARE_VTABLE_PTR_HELPER_CTOR为默认构造函数,Vtable的定义。接下来我们一个一个拆这看。

RPC_WRAPPERS_NO_PURE_DECLS

cpp 复制代码
#define FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_RPC_WRAPPERS \
 \
	DECLARE_FUNCTION(execPtLog);

我们同样可以在generated.h文件中找到这个宏的定义,发现是又套娃了一层DECLARE_FUNCTION的宏,宏的参数就是UFNCTION函数名,接着看看DECLARE_FUNCTION这个宏又做了什么。

cpp 复制代码
#define DECLARE_FUNCTION(func) static void func( UObject* Context, FFrame& Stack, RESULT_DECL )

可以发现UFUNCTION宏会被UHT生成一个类内静态函数,这个类内静态函数的实现在对应的gen.cpp文件中

cpp 复制代码
DEFINE_FUNCTION(func) void func( UObject* Context, FFrame& Stack, RESULT_DECL )

	DEFINE_FUNCTION(AGhost::execPtLog)
	{
		P_GET_PROPERTY(FIntProperty,Z_Param_i);
		P_FINISH;
		P_NATIVE_BEGIN;
		P_THIS->PtLog(Z_Param_i);
		P_NATIVE_END;
	}

把这段全是宏的代码展开

cpp 复制代码
void AGhost::execPtLog( UObject* Context, FFrame& Stack, RESULT_DECL )
{
	FIntProperty::TCppType Z_Param_i = PropertyType::GetDefaultPropertyValue();
	Stack.StepCompiledIn<FIntProperty>(&Z_Param_i);
	Stack.Code += !!Stack.Code;
	((AGhost*)(Context))->PtLog(Z_Param_i);
}

Context其实就是执行AGhost实体,参数类型为UObject*,但是实际上是AGhost*,这里利用的UObject做类型擦除,就和void*一样。 Stack为参数,比较好理解。

这个函数会由对应的UFunction::Invoke函数调用,完成反射函数的调用。

ACCESSORS

暂时不知道是做什么,pass

INCLASS_NO_PURE_DECLS

cpp 复制代码
#define FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_INCLASS_NO_PURE_DECLS \
private: \
	static void StaticRegisterNativesAGhost(); \
	friend struct Z_Construct_UClass_AGhost_Statics; \
public: \
	DECLARE_CLASS(AGhost, AActor, COMPILED_IN_FLAGS(0 | CLASS_Config), CASTCLASS_None, TEXT("/Script/learnUe"), NO_API) \
	DECLARE_SERIALIZER(AGhost) \
	static const TCHAR* StaticConfigName() {return TEXT("Ghost");} \

StaticRegisterNativesAGhost()函数的作用是向自己对应的Uclass类注册UFUNCTION,这个函数的定义在对应的gen.cpp中

cpp 复制代码
void AGhost::StaticRegisterNativesAGhost()
	{
		UClass* Class = AGhost::StaticClass();
		static const FNameNativePtrPair Funcs[] = {
			{ "PtLog", &AGhost::execPtLog },
		};
		FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs));
	}

DECLARE_CLASS宏则是定义一些UE类必有的内容,如StaticClass,StaticPackage,new,delete等必要的东西。

DECLARE_SERIALIZER宏看名字就很容易猜出来,必然是与序列化相关

cpp 复制代码
#define DECLARE_SERIALIZER( TClass ) \
	friend FArchive &operator<<( FArchive& Ar, TClass*& Res ) \
	{ \
		return Ar << (UObject*&)Res; \
	} \
	friend void operator<<(FStructuredArchive::FSlot InSlot, TClass*& Res) \
	{ \
		InSlot << (UObject*&)Res; \
	}

STANDARD_CONSTRUCTORS

cpp 复制代码
#define FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_ENHANCED_CONSTRUCTORS \
private: \
	/** Private move- and copy-constructors, should never be used */ \
	NO_API AGhost(AGhost&&); \
	NO_API AGhost(const AGhost&); \
public: \
	DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, AGhost); \
	DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(AGhost); \
	DEFINE_DEFAULT_CONSTRUCTOR_CALL(AGhost)

STANDARD_CONSTRUCTORS宏禁止了移动和拷贝,并定义了DEAFULT_CONSTRUCTOR,需要注意的是,这个并不是定义了默认的构造函数

cpp 复制代码
#define DEFINE_DEFAULT_CONSTRUCTOR_CALL(TClass) \
	static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass; }

到此为止.h和gen.h文件中的宏就展开完毕了。

反射信息注册

ue中反射信息是分段注册的,由static结构体的构造为第一段,执行在main函数之前,第二段在ue启动时完成真正的注册。

反射的信息几乎都在对应类的gen.cpp文件中,这也是由UHT生成的。

在gen.cpp文件中ue会生成一个每个类唯一的FClassRegistrationInfo变量,这个变量与GetPrivateStaticClass函数定义在gen.cpp文件中的IMPLEMENT_CLASS_NO_AUTO_REGISTRATION宏中,这个变量就包含了类的信息。

FClassRegistrationInfo

cpp 复制代码
IMPLEMENT_CLASS_NO_AUTO_REGISTRATION(AGhost);

#define IMPLEMENT_CLASS_NO_AUTO_REGISTRATION(TClass) \
	FClassRegistrationInfo Z_Registration_Info_UClass_##TClass; \
	UClass* TClass::GetPrivateStaticClass() \
	{ \
		if (!Z_Registration_Info_UClass_##TClass.InnerSingleton) \
		{ \
			/* 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), \
				Z_Registration_Info_UClass_##TClass.InnerSingleton, \
				StaticRegisterNatives##TClass, \
				sizeof(TClass), \
				alignof(TClass), \
				(EClassFlags)TClass::StaticClassFlags, \
				TClass::StaticClassCastFlags(), \
				TClass::StaticConfigName(), \
				(UClass::ClassConstructorType)InternalConstructor<TClass>, \
				(UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<TClass>, \
				UOBJECT_CPPCLASS_STATICFUNCTIONS_FORCLASS(TClass), \
				&TClass::Super::StaticClass, \
				&TClass::WithinClass::StaticClass \
			); \
		} \
		return Z_Registration_Info_UClass_##TClass.InnerSingleton; \
	}

IMPLEMENT_CLASS_NO_AUTO_REGISTRATION宏中定义了FClassRegistrationInfo类的对象和GetPrivateStaticClass函数,GetPrivateStaticClass函数可能大家不熟,但是StaticClass大家一定知道,其实StaticClass内部调用的就是GetPrivateStaticClass函数。

GetPrivateStaticClassBody的本质是在第一次调用时通过GetPrivateStaticClassBody函数生成一个uclass给Z_Registration_Info_UClass_##TClass.InnerSingleton参数。

GetPrivateStaticClassBody函数是比较长的,但是也比较简单

cpp 复制代码
void GetPrivateStaticClassBody(
	const TCHAR* PackageName,
	const TCHAR* Name,
	UClass*& ReturnClass,
	void(*RegisterNativeFunc)(),
	uint32 InSize,
	uint32 InAlignment,
	EClassFlags InClassFlags,
	EClassCastFlags InClassCastFlags,
	const TCHAR* InConfigName,
	UClass::ClassConstructorType InClassConstructor,
	UClass::ClassVTableHelperCtorCallerType InClassVTableHelperCtorCaller,
	FUObjectCppClassStaticFunctions&& InCppClassStaticFunctions,
	UClass::StaticClassFunctionType InSuperClassFn,
	UClass::StaticClassFunctionType InWithinClassFn
	)
{
#if WITH_RELOAD
	if (IsReloadActive() && GetActiveReloadType() != EActiveReloadType::Reinstancing)
	{
		UPackage* Package = FindPackage(NULL, PackageName);
		if (Package)
		{
			ReturnClass = FindObject<UClass>((UObject *)Package, Name);
			if (ReturnClass)
			{
				if (ReturnClass->HotReloadPrivateStaticClass(
					InSize,
					InClassFlags,
					InClassCastFlags,
					InConfigName,
					InClassConstructor,
					InClassVTableHelperCtorCaller,
					FUObjectCppClassStaticFunctions(InCppClassStaticFunctions),
					InSuperClassFn(),
					InWithinClassFn()
					))
				{
					// Register the class's native functions.
					RegisterNativeFunc();
				}
				return;
			}
			else
			{
				UE_LOG(LogClass, Log, TEXT("Could not find existing class %s in package %s for reload, assuming new or modified class"), Name, PackageName);
			}
		}
		else
		{
			UE_LOG(LogClass, Log, TEXT("Could not find existing package %s for reload of class %s, assuming a new package."), PackageName, Name);
		}
	}
#endif

	ReturnClass = (UClass*)GUObjectAllocator.AllocateUObject(sizeof(UClass), alignof(UClass), true);
	ReturnClass = ::new (ReturnClass)
		UClass
		(
		EC_StaticConstructor,
		Name,
		InSize,
		InAlignment,
		InClassFlags,
		InClassCastFlags,
		InConfigName,
		EObjectFlags(RF_Public | RF_Standalone | RF_Transient | RF_MarkAsNative | RF_MarkAsRootSet),
		InClassConstructor,
		InClassVTableHelperCtorCaller,
		MoveTemp(InCppClassStaticFunctions)
		);
	check(ReturnClass);
	
	InitializePrivateStaticClass(
		InSuperClassFn(),
		ReturnClass,
		InWithinClassFn(),
		PackageName,
		Name
		);

	// Register the class's native functions.
	RegisterNativeFunc();
}

简单来说就是如果是热重载则执行HotReloadPrivateStaticClass,如果不是则new一个uclass类型出来,然后如果有继承关系则设置子父类的关系,最后调用参数RegisterNativeFunc函数。RegisterNativeFunc为gen.cpp文件中的StaticRegisterNatives开头加类名的函数,作用是注册类内拥有UFUNCTION宏的函数,

cpp 复制代码
	void AGhost::StaticRegisterNativesAGhost()
	{
		UClass* Class = AGhost::StaticClass();
		static const FNameNativePtrPair Funcs[] = {
			{ "PtLog", &AGhost::execPtLog },
		};
		FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs));
	}

利用static变量

ue利用static变量的初始化进行预注册,预注册指的是仅收集谁是要注册的类,不进行真正的注册,真正的注册等到程序起来后在进行,我猜测ue不在static初始化时就注册完全,是因为如果都在static初始化时注册程序启动时耗费时间比较长,会被误以为卡死之类的。

接下来我们来看ue是怎么利用static变量的初始化进行注册的。在gen.cpp文件中有着这么一个静态变量

cpp 复制代码
	static FRegisterCompiledInInfo Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_3523060098(TEXT("/Script/learnUe"),
		Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo, UE_ARRAY_COUNT(Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo),
		nullptr, 0,
		nullptr, 0);

FRegisterCompiledInInfo类型就是ue封装好的一个,我们看下它的本质

cpp 复制代码
struct FRegisterCompiledInInfo
{
	template <typename ... Args>
	FRegisterCompiledInInfo(Args&& ... args)
	{
		RegisterCompiledInInfo(std::forward<Args>(args)...);
	}
};

void RegisterCompiledInInfo(class UClass* (*InOuterRegister)(), class UClass* (*InInnerRegister)(), const TCHAR* InPackageName, const TCHAR* InName, FClassRegistrationInfo& InInfo, const FClassReloadVersionInfo& InVersionInfo)
{
	check(InOuterRegister);
	check(InInnerRegister);
	FClassDeferredRegistry::AddResult result = FClassDeferredRegistry::Get().AddRegistration(InOuterRegister, InInnerRegister, InPackageName, InName, InInfo, InVersionInfo);
#if WITH_RELOAD
	if (result == FClassDeferredRegistry::AddResult::ExistingChanged && !IsReloadActive())
	{
		// Class exists, this can only happen during hot-reload or live coding
		UE_LOG(LogUObjectBase, Fatal, TEXT("Trying to recreate changed class '%s' outside of hot reload and live coding!"), InName);
	}
#endif
	FString NoPrefix(UObjectBase::RemoveClassPrefix(InName));
	NotifyRegistrationEvent(InPackageName, *NoPrefix, ENotifyRegistrationType::NRT_Class, ENotifyRegistrationPhase::NRP_Added, (UObject * (*)())(InOuterRegister), false);
	NotifyRegistrationEvent(InPackageName, *(FString(DEFAULT_OBJECT_PREFIX) + NoPrefix), ENotifyRegistrationType::NRT_ClassCDO, ENotifyRegistrationPhase::NRP_Added, (UObject * (*)())(InOuterRegister), false);
}

在构造时候则执行RegisterCompiledInInfo函数,顺带转发参数。顺带一提,RegisterCompiledInInfo函数有多个重载,我这里仅展示UCLASS的重载,实际上UEnum,UStruc调用的都是不同的RegisterCompiledInInfo函数,

FStructDeferredRegistry可以简单理解成一个模板数组,数组里存的是AddRegistration函数的参数信息。虽然实际上FStructDeferredRegistry内部就是数组结构,但是内部信息做了一定的封装。在ue中class,enum,stuct和package都有自己的DeferredRegistry,如FClassDeferredRegistry的声明处。

cpp 复制代码
using FClassDeferredRegistry = TDeferredRegistry<FClassRegistrationInfo>;
using FEnumDeferredRegistry = TDeferredRegistry<FEnumRegistrationInfo>;
using FStructDeferredRegistry = TDeferredRegistry<FStructRegistrationInfo>;
using FPackageDeferredRegistry = TDeferredRegistry<FPackageRegistrationInfo>;

接着AddRegistration函数和NotifyRegistrationEvent分别注册了InOuterRegister的函数指针和InInnerRegister函数指针,这个两个函数指针实际上也是gen.cpp文件中生成的函数。分别为Z_Construct_UClass+类名的函数和StaticClass函数,不过这里先放一放。我们先看AddRegistration函数做了什么。

cpp 复制代码
/// <summary>
	/// Adds the given registration information for the given object.  Objects are either classes, structs, or enumerations.
	/// </summary>
	/// <param name="InOuterRegister">Returns a fully initialize instance of the object</param>
	/// <param name="InInnerRegister">Returns an allocated but uninitialized instance of the object.  This is used only by UClass.</param>
	/// <param name="InPackageName">Name of the package</param>
	/// <param name="InName">Name of the object</param>
	/// <param name="InInfo">Persistent information about the object</param>
	/// <param name="InVersion">Version information for this incarnation of the object</param>
	AddResult AddRegistration(TType* (*InOuterRegister)(), TType* (*InInnerRegister)(), const TCHAR* InPackageName, const TCHAR* InName, TInfo& InInfo, const TVersion& InVersion)
	{
#if WITH_RELOAD
		const FPackageAndNameKey Key = FPackageAndNameKey{ InPackageName, InName };
		TInfo** ExistingInfo = InfoMap.Find(Key);

		bool bHasChanged = !ExistingInfo || (*ExistingInfo)->ReloadVersionInfo != InVersion;
		InInfo.ReloadVersionInfo = InVersion;
		TType* OldSingleton = ExistingInfo ? ((*ExistingInfo)->InnerSingleton ? (*ExistingInfo)->InnerSingleton : (*ExistingInfo)->OuterSingleton) : nullptr;

		bool bAdd = true;
		if (ExistingInfo)
		{
			if (IReload* Reload = GetActiveReloadInterface())
			{
				bAdd = Reload->GetEnableReinstancing(bHasChanged);
			}
			if (bAdd)
			{
				if (!bHasChanged)
				{
					// With live coding, the existing might be the same as the new info.  
					// We still invoke the copy method to allow UClasses to clear the singletons.
					UpdateSingletons(InInfo, **ExistingInfo);
				}
				*ExistingInfo = &InInfo;
			}
		}
		else
		{
			InfoMap.Add(Key, &InInfo);
		}
		if (bAdd)
		{
			Registrations.Add(FRegistrant{ InOuterRegister, InInnerRegister, InPackageName, &InInfo, OldSingleton, bHasChanged });
		}
		return ExistingInfo == nullptr ? AddResult::New : (bHasChanged ? AddResult::ExistingChanged : AddResult::ExistingNoChange);
#else
		Registrations.Add(FRegistrant{ InOuterRegister, InInnerRegister, InPackageName, &InInfo });
		return AddResult::New;
#endif
	}

AddRegistration函数的逻辑是现在自身map上找一下是否存在要add的类,如果有则看下是否为热重载,如果没有则加入map,最后在把信息包装成FRegistrant放入自身的Registrations数组中,待后续使用。

然后回到RegisterCompiledInInfo函数,在AddRegistration后又调用了NotifyRegistrationEvent函数,看名字是通知什么模块有注册事件。

cpp 复制代码
TUniquePtr<IAsyncPackageLoader> GPackageLoader;

void NotifyRegistrationEvent(const TCHAR* PackageName, const TCHAR* Name, ENotifyRegistrationType NotifyRegistrationType, ENotifyRegistrationPhase NotifyRegistrationPhase, UObject *(*InRegister)(), bool InbDynamic, UObject* FinishedObject)
{
	LLM_SCOPE(ELLMTag::AsyncLoading);

	if (GPackageLoader)
	{
		GPackageLoader->NotifyRegistrationEvent(PackageName, Name, NotifyRegistrationType, NotifyRegistrationPhase, InRegister, InbDynamic, FinishedObject);
	}
	else
	{
		GetEarlyRegistrationEventsRecorder().NotifyRegistrationEvent(PackageName, Name, NotifyRegistrationType, NotifyRegistrationPhase, InRegister, InbDynamic, FinishedObject);
	}
}

值得注意的是这里GPackageLoader的类型,声明上是IAsyncPackageLoader,但是实际上是FAsyncLoadingThread,这两个类是父子关系,函数为虚函数,c++里很经典的一种用法,所以GPackageLoader->NotifyRegistrationEvent调用的实际上是FAsyncLoadingThread->NotifyRegistrationEvent,来看下这个函数做了什么

cpp 复制代码
struct FEDLBootNotificationManager& EDLBootNotificationManager;

void FAsyncLoadingThread::NotifyRegistrationEvent(const TCHAR* PackageName, const TCHAR* Name, ENotifyRegistrationType NotifyRegistrationType, ENotifyRegistrationPhase NotifyRegistrationPhase, UObject* (*InRegister)(), bool InbDynamic, UObject* FinishedObject)
{
	EDLBootNotificationManager.NotifyRegistrationEvent(PackageName, Name, NotifyRegistrationType, NotifyRegistrationPhase, InRegister, InbDynamic);
}

emmm看这变量名字,是这EDLBoot又代表啥缩写,晕,查了下大概一个ue启动时辅助初始化的东东。暂时不太明白作用。

inner注册

我们知道了ue利用static变量在main函数之前注册了一些信息,现在我来看看ue是如使用这些DeferredRegistry 变量的。

在ue启动时,大概的时间点是在loadModule时,会执行一个一个叫做UClassRegisterAllCompiledInClasses的函数。

cpp 复制代码
void UClassRegisterAllCompiledInClasses()
{
#if WITH_RELOAD
	TArray<UClass*> AddedClasses;
#endif
	SCOPED_BOOT_TIMING("UClassRegisterAllCompiledInClasses");

	FClassDeferredRegistry& Registry = FClassDeferredRegistry::Get();

	Registry.ProcessChangedObjects();

	for (const FClassDeferredRegistry::FRegistrant& Registrant : Registry.GetRegistrations())
	{
		UClass* RegisteredClass = FClassDeferredRegistry::InnerRegister(Registrant);
#if WITH_RELOAD
		if (IsReloadActive() && Registrant.OldSingleton == nullptr)
		{
			AddedClasses.Add(RegisteredClass);
		}
#endif
	}

#if WITH_RELOAD
	if (AddedClasses.Num() > 0)
	{
		FCoreUObjectDelegates::ReloadAddedClassesDelegate.Broadcast(AddedClasses);
		PRAGMA_DISABLE_DEPRECATION_WARNINGS
		FCoreUObjectDelegates::RegisterHotReloadAddedClassesDelegate.Broadcast(AddedClasses);
		PRAGMA_ENABLE_DEPRECATION_WARNINGS
	}
#endif
}

FClassDeferredRegistry::Get()其实就是获取单例,然后将之前利用static变量注册进去的类,执行InnerRegiste函数指针,这个InnerRegiste是之前gen.cpp文件中的那个StaticClass函数,其内部就是我们一开始说的GetPrivateStaticClass函数。它实例化出了uclass类并赋予信息,但是到这里还没有完,到这里uclass仅仅只有一些ue对类的附加信息,如flag,configname等,类内成员和类内函数的信息没有注册到uclass,如果细心一点会注意到,gen。cpp文件中不仅有inner,还有outer,

后面的outer就是注册类内成员和类内成员函数的

outer注册

在gen.cpp的文件中有着这样的一个函数,它名字是Z_Construct_UClass_+类名,

在我的AGhost类中它为Z_Construct_UClass_AGhost

cpp 复制代码
UClass* Z_Construct_UClass_AGhost()
	{
		if (!Z_Registration_Info_UClass_AGhost.OuterSingleton)
		{
			UECodeGen_Private::ConstructUClass(Z_Registration_Info_UClass_AGhost.OuterSingleton, Z_Construct_UClass_AGhost_Statics::ClassParams);
		}
		return Z_Registration_Info_UClass_AGhost.OuterSingleton;
	}

内部使用了UECodeGen_Private::ConstructUClass进行构造outer,参数呢是熟悉的Z_Registration_Info_UClass_AGhost,就是那个staticclass函数里用的那个。同时也是和inner一样只需要构建一次。

我们接下来看看这个ConstructUClass做了什么

cpp 复制代码
void ConstructUClass(UClass*& OutClass, const FClassParams& Params)
	{
		if (OutClass && (OutClass->ClassFlags & CLASS_Constructed))
		{
			return;
		}

		for (UObject* (*const *SingletonFunc)() = Params.DependencySingletonFuncArray, *(*const *SingletonFuncEnd)() = SingletonFunc + Params.NumDependencySingletons; SingletonFunc != SingletonFuncEnd; ++SingletonFunc)
		{
			(*SingletonFunc)();
		}

		UClass* NewClass = Params.ClassNoRegisterFunc();
		OutClass = NewClass;

		if (NewClass->ClassFlags & CLASS_Constructed)
		{
			return;
		}

		UObjectForceRegistration(NewClass);

		UClass* SuperClass = NewClass->GetSuperClass();
		if (SuperClass)
		{
			NewClass->ClassFlags |= (SuperClass->ClassFlags & CLASS_Inherit);
		}

		NewClass->ClassFlags |= (EClassFlags)(Params.ClassFlags | CLASS_Constructed);
		// Make sure the reference token stream is empty since it will be reconstructed later on
		// This should not apply to intrinsic classes since they emit native references before AssembleReferenceTokenStream is called.
		if ((NewClass->ClassFlags & CLASS_Intrinsic) != CLASS_Intrinsic)
		{
			check((NewClass->ClassFlags & CLASS_TokenStreamAssembled) != CLASS_TokenStreamAssembled);
			NewClass->ReferenceTokenStream.Empty();
		}
		NewClass->CreateLinkAndAddChildFunctionsToMap(Params.FunctionLinkArray, Params.NumFunctions);

		ConstructFProperties(NewClass, Params.PropertyArray, Params.NumProperties);

		if (Params.ClassConfigNameUTF8)
		{
			NewClass->ClassConfigName = FName(UTF8_TO_TCHAR(Params.ClassConfigNameUTF8));
		}

		NewClass->SetCppTypeInfoStatic(Params.CppClassInfo);

		if (int32 NumImplementedInterfaces = Params.NumImplementedInterfaces)
		{
			NewClass->Interfaces.Reserve(NumImplementedInterfaces);
			for (const FImplementedInterfaceParams* ImplementedInterface = Params.ImplementedInterfaceArray, *ImplementedInterfaceEnd = ImplementedInterface + NumImplementedInterfaces; ImplementedInterface != ImplementedInterfaceEnd; ++ImplementedInterface)
			{
				UClass* (*ClassFunc)() = ImplementedInterface->ClassFunc;
				UClass* InterfaceClass = ClassFunc ? ClassFunc() : nullptr;

				NewClass->Interfaces.Emplace(InterfaceClass, ImplementedInterface->Offset, ImplementedInterface->bImplementedByK2);
			}
		}

#if WITH_METADATA
		AddMetaData(NewClass, Params.MetaDataArray, Params.NumMetaData);
#endif

		NewClass->StaticLink();

		NewClass->SetSparseClassDataStruct(NewClass->GetSparseClassDataArchetypeStruct());
	}
}

可以看到首先执行了parm.DependencySingletonFuncArray参数中所有的函数指针,而这个parm在gen.cpp文件是个叫ClassParams的FClassParmas的结构体,里面存放着UFuntion的信息。

cpp 复制代码
const UECodeGen_Private::FClassParams Z_Construct_UClass_AGhost_Statics::ClassParams = {
		&AGhost::StaticClass,
		"Ghost",
		&StaticCppClassTypeInfo,
		DependentSingletons,
		FuncInfo,
		Z_Construct_UClass_AGhost_Statics::PropPointers,
		nullptr,
		UE_ARRAY_COUNT(DependentSingletons),
		UE_ARRAY_COUNT(FuncInfo),
		UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::PropPointers),
		0,
		0x009000A4u,
		METADATA_PARAMS(Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams, UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams))
	};
	//FclassParms定义
	struct FClassParams
	{
		UClass*                                   (*ClassNoRegisterFunc)();
		const char*                                 ClassConfigNameUTF8;
		const FCppClassTypeInfoStatic*              CppClassInfo;
		UObject*                           (*const *DependencySingletonFuncArray)();
		const FClassFunctionLinkInfo*               FunctionLinkArray;
		const FPropertyParamsBase* const*           PropertyArray;
		const FImplementedInterfaceParams*          ImplementedInterfaceArray;
		int32                                       NumDependencySingletons;
		int32                                       NumFunctions;
		int32                                       NumProperties;
		int32                                       NumImplementedInterfaces;
		uint32                                      ClassFlags; // EClassFlags
#if WITH_METADATA
		const FMetaDataPairParam*                   MetaDataArray;
		int32                                       NumMetaData;
#endif
	};

ConstructUClass函数主要就是利用FClassParams去构建Uclass的信息,如果全部展开篇幅实在是太太长了,我也写不动啦,想摸了。有兴趣可以自行找下生成的gen.cpp文件,基本都在那里啦。

副案例代码

cpp 复制代码
//```cpp
//Ghost.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 "learnUe/Ghost.h"
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void EmptyLinkFunctionForGeneratedCodeGhost() {}
// Cross Module References
	ENGINE_API UClass* Z_Construct_UClass_AActor();
	LEARNUE_API UClass* Z_Construct_UClass_AGhost();
	LEARNUE_API UClass* Z_Construct_UClass_AGhost_NoRegister();
	UPackage* Z_Construct_UPackage__Script_learnUe();
// End Cross Module References
	DEFINE_FUNCTION(AGhost::execPtLog)
	{
		P_GET_PROPERTY(FIntProperty,Z_Param_i);
		P_FINISH;
		P_NATIVE_BEGIN;
		P_THIS->PtLog(Z_Param_i);
		P_NATIVE_END;
	}
	void AGhost::StaticRegisterNativesAGhost()
	{
		UClass* Class = AGhost::StaticClass();
		static const FNameNativePtrPair Funcs[] = {
			{ "PtLog", &AGhost::execPtLog },
		};
		FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs));
	}
	struct Z_Construct_UFunction_AGhost_PtLog_Statics
	{
		struct Ghost_eventPtLog_Parms
		{
			int32 i;
		};
		static const UECodeGen_Private::FUnsizedIntPropertyParams NewProp_i;
		static const UECodeGen_Private::FPropertyParamsBase* const PropPointers[];
#if WITH_METADATA
		static const UECodeGen_Private::FMetaDataPairParam Function_MetaDataParams[];
#endif
		static const UECodeGen_Private::FFunctionParams FuncParams;
	};
	const UECodeGen_Private::FUnsizedIntPropertyParams Z_Construct_UFunction_AGhost_PtLog_Statics::NewProp_i = { "i", nullptr, (EPropertyFlags)0x0010000000000080, UECodeGen_Private::EPropertyGenFlags::Int, RF_Public|RF_Transient|RF_MarkAsNative, 1, nullptr, nullptr, STRUCT_OFFSET(Ghost_eventPtLog_Parms, i), METADATA_PARAMS(nullptr, 0) };
	const UECodeGen_Private::FPropertyParamsBase* const Z_Construct_UFunction_AGhost_PtLog_Statics::PropPointers[] = {
		(const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UFunction_AGhost_PtLog_Statics::NewProp_i,
	};
#if WITH_METADATA
	const UECodeGen_Private::FMetaDataPairParam Z_Construct_UFunction_AGhost_PtLog_Statics::Function_MetaDataParams[] = {
		{ "ModuleRelativePath", "Ghost.h" },
	};
#endif
	const UECodeGen_Private::FFunctionParams Z_Construct_UFunction_AGhost_PtLog_Statics::FuncParams = { (UObject*(*)())Z_Construct_UClass_AGhost, nullptr, "PtLog", nullptr, nullptr, sizeof(Z_Construct_UFunction_AGhost_PtLog_Statics::Ghost_eventPtLog_Parms), Z_Construct_UFunction_AGhost_PtLog_Statics::PropPointers, UE_ARRAY_COUNT(Z_Construct_UFunction_AGhost_PtLog_Statics::PropPointers), RF_Public|RF_Transient|RF_MarkAsNative, (EFunctionFlags)0x04020401, 0, 0, METADATA_PARAMS(Z_Construct_UFunction_AGhost_PtLog_Statics::Function_MetaDataParams, UE_ARRAY_COUNT(Z_Construct_UFunction_AGhost_PtLog_Statics::Function_MetaDataParams)) };
	UFunction* Z_Construct_UFunction_AGhost_PtLog()
	{
		static UFunction* ReturnFunction = nullptr;
		if (!ReturnFunction)
		{
			UECodeGen_Private::ConstructUFunction(&ReturnFunction, Z_Construct_UFunction_AGhost_PtLog_Statics::FuncParams);
		}
		return ReturnFunction;
	}
	IMPLEMENT_CLASS_NO_AUTO_REGISTRATION(AGhost);
	UClass* Z_Construct_UClass_AGhost_NoRegister()
	{
		return AGhost::StaticClass();
	}
	struct Z_Construct_UClass_AGhost_Statics
	{
		static UObject* (*const DependentSingletons[])();
		static const FClassFunctionLinkInfo FuncInfo[];
#if WITH_METADATA
		static const UECodeGen_Private::FMetaDataPairParam Class_MetaDataParams[];
#endif
#if WITH_METADATA
		static const UECodeGen_Private::FMetaDataPairParam NewProp_m_time_MetaData[];
#endif
		static const UECodeGen_Private::FIntPropertyParams NewProp_m_time;
		static const UECodeGen_Private::FPropertyParamsBase* const PropPointers[];
		static const FCppClassTypeInfoStatic StaticCppClassTypeInfo;
		static const UECodeGen_Private::FClassParams ClassParams;
	};
	UObject* (*const Z_Construct_UClass_AGhost_Statics::DependentSingletons[])() = {
		(UObject* (*)())Z_Construct_UClass_AActor,
		(UObject* (*)())Z_Construct_UPackage__Script_learnUe,
	};
	const FClassFunctionLinkInfo Z_Construct_UClass_AGhost_Statics::FuncInfo[] = {
		{ &Z_Construct_UFunction_AGhost_PtLog, "PtLog" }, // 1286413536
	};
#if WITH_METADATA
	const UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams[] = {
		{ "IncludePath", "Ghost.h" },
		{ "ModuleRelativePath", "Ghost.h" },
	};
#endif
#if WITH_METADATA
	const UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_AGhost_Statics::NewProp_m_time_MetaData[] = {
		{ "Category", "Ghost" },
		{ "ModuleRelativePath", "Ghost.h" },
	};
#endif
	const UECodeGen_Private::FIntPropertyParams Z_Construct_UClass_AGhost_Statics::NewProp_m_time = { "m_time", nullptr, (EPropertyFlags)0x0010000000020001, UECodeGen_Private::EPropertyGenFlags::Int, RF_Public|RF_Transient|RF_MarkAsNative, 1, nullptr, nullptr, STRUCT_OFFSET(AGhost, m_time), METADATA_PARAMS(Z_Construct_UClass_AGhost_Statics::NewProp_m_time_MetaData, UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::NewProp_m_time_MetaData)) };
	const UECodeGen_Private::FPropertyParamsBase* const Z_Construct_UClass_AGhost_Statics::PropPointers[] = {
		(const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_AGhost_Statics::NewProp_m_time,
	};
	const FCppClassTypeInfoStatic Z_Construct_UClass_AGhost_Statics::StaticCppClassTypeInfo = {
		TCppClassTypeTraits<AGhost>::IsAbstract,
	};
	const UECodeGen_Private::FClassParams Z_Construct_UClass_AGhost_Statics::ClassParams = {
		&AGhost::StaticClass,
		"Ghost",
		&StaticCppClassTypeInfo,
		DependentSingletons,
		FuncInfo,
		Z_Construct_UClass_AGhost_Statics::PropPointers,
		nullptr,
		UE_ARRAY_COUNT(DependentSingletons),
		UE_ARRAY_COUNT(FuncInfo),
		UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::PropPointers),
		0,
		0x009000A4u,
		METADATA_PARAMS(Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams, UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams))
	};
	UClass* Z_Construct_UClass_AGhost()
	{
		if (!Z_Registration_Info_UClass_AGhost.OuterSingleton)
		{
			UECodeGen_Private::ConstructUClass(Z_Registration_Info_UClass_AGhost.OuterSingleton, Z_Construct_UClass_AGhost_Statics::ClassParams);
		}
		return Z_Registration_Info_UClass_AGhost.OuterSingleton;
	}
	template<> LEARNUE_API UClass* StaticClass<AGhost>()
	{
		return AGhost::StaticClass();
	}
	DEFINE_VTABLE_PTR_HELPER_CTOR(AGhost);
	struct Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics
	{
		static const FClassRegisterCompiledInInfo ClassInfo[];
	};
	const FClassRegisterCompiledInInfo Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo[] = {
		{ Z_Construct_UClass_AGhost, AGhost::StaticClass, TEXT("AGhost"), &Z_Registration_Info_UClass_AGhost, CONSTRUCT_RELOAD_VERSION_INFO(FClassReloadVersionInfo, sizeof(AGhost), 1620684469U) },
	};
	static FRegisterCompiledInInfo Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_3523060098(TEXT("/Script/learnUe"),
		Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo, UE_ARRAY_COUNT(Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo),
		nullptr, 0,
		nullptr, 0);
PRAGMA_ENABLE_DEPRECATION_WARNINGS

```Ghost.h
UCLASS(config = Ghost)
class LEARNUE_API AGhost : public AActor, public FTickableGameObject
{
	GENERATED_BODY()
public:
	virtual void BeginPlay() override;
	virtual void Tick(float DeltaTime) override;
	virtual TStatId GetStatId() const override
	{
		return Super::GetStatID();
	}
	
public:
	AGhost();
	virtual  ~AGhost() override;

	UFUNCTION(BlueprintCallable)
	void PtLog(int i);

public:
	FGhostDelegate m_ghostDelegate;
	UPROPERTY(VisibleAnywhere)
	int32 m_time ;
};
cpp 复制代码
//Ghost.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 "learnUe/Ghost.h"
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void EmptyLinkFunctionForGeneratedCodeGhost() {}
// Cross Module References
	ENGINE_API UClass* Z_Construct_UClass_AActor();
	LEARNUE_API UClass* Z_Construct_UClass_AGhost();
	LEARNUE_API UClass* Z_Construct_UClass_AGhost_NoRegister();
	UPackage* Z_Construct_UPackage__Script_learnUe();
// End Cross Module References
	DEFINE_FUNCTION(AGhost::execPtLog)
	{
		P_GET_PROPERTY(FIntProperty,Z_Param_i);
		P_FINISH;
		P_NATIVE_BEGIN;
		P_THIS->PtLog(Z_Param_i);
		P_NATIVE_END;
	}
	void AGhost::StaticRegisterNativesAGhost()
	{
		UClass* Class = AGhost::StaticClass();
		static const FNameNativePtrPair Funcs[] = {
			{ "PtLog", &AGhost::execPtLog },
		};
		FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs));
	}
	struct Z_Construct_UFunction_AGhost_PtLog_Statics
	{
		struct Ghost_eventPtLog_Parms
		{
			int32 i;
		};
		static const UECodeGen_Private::FUnsizedIntPropertyParams NewProp_i;
		static const UECodeGen_Private::FPropertyParamsBase* const PropPointers[];
#if WITH_METADATA
		static const UECodeGen_Private::FMetaDataPairParam Function_MetaDataParams[];
#endif
		static const UECodeGen_Private::FFunctionParams FuncParams;
	};
	const UECodeGen_Private::FUnsizedIntPropertyParams Z_Construct_UFunction_AGhost_PtLog_Statics::NewProp_i = { "i", nullptr, (EPropertyFlags)0x0010000000000080, UECodeGen_Private::EPropertyGenFlags::Int, RF_Public|RF_Transient|RF_MarkAsNative, 1, nullptr, nullptr, STRUCT_OFFSET(Ghost_eventPtLog_Parms, i), METADATA_PARAMS(nullptr, 0) };
	const UECodeGen_Private::FPropertyParamsBase* const Z_Construct_UFunction_AGhost_PtLog_Statics::PropPointers[] = {
		(const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UFunction_AGhost_PtLog_Statics::NewProp_i,
	};
#if WITH_METADATA
	const UECodeGen_Private::FMetaDataPairParam Z_Construct_UFunction_AGhost_PtLog_Statics::Function_MetaDataParams[] = {
		{ "ModuleRelativePath", "Ghost.h" },
	};
#endif
	const UECodeGen_Private::FFunctionParams Z_Construct_UFunction_AGhost_PtLog_Statics::FuncParams = { (UObject*(*)())Z_Construct_UClass_AGhost, nullptr, "PtLog", nullptr, nullptr, sizeof(Z_Construct_UFunction_AGhost_PtLog_Statics::Ghost_eventPtLog_Parms), Z_Construct_UFunction_AGhost_PtLog_Statics::PropPointers, UE_ARRAY_COUNT(Z_Construct_UFunction_AGhost_PtLog_Statics::PropPointers), RF_Public|RF_Transient|RF_MarkAsNative, (EFunctionFlags)0x04020401, 0, 0, METADATA_PARAMS(Z_Construct_UFunction_AGhost_PtLog_Statics::Function_MetaDataParams, UE_ARRAY_COUNT(Z_Construct_UFunction_AGhost_PtLog_Statics::Function_MetaDataParams)) };
	UFunction* Z_Construct_UFunction_AGhost_PtLog()
	{
		static UFunction* ReturnFunction = nullptr;
		if (!ReturnFunction)
		{
			UECodeGen_Private::ConstructUFunction(&ReturnFunction, Z_Construct_UFunction_AGhost_PtLog_Statics::FuncParams);
		}
		return ReturnFunction;
	}
	IMPLEMENT_CLASS_NO_AUTO_REGISTRATION(AGhost);
	UClass* Z_Construct_UClass_AGhost_NoRegister()
	{
		return AGhost::StaticClass();
	}
	struct Z_Construct_UClass_AGhost_Statics
	{
		static UObject* (*const DependentSingletons[])();
		static const FClassFunctionLinkInfo FuncInfo[];
#if WITH_METADATA
		static const UECodeGen_Private::FMetaDataPairParam Class_MetaDataParams[];
#endif
#if WITH_METADATA
		static const UECodeGen_Private::FMetaDataPairParam NewProp_m_time_MetaData[];
#endif
		static const UECodeGen_Private::FIntPropertyParams NewProp_m_time;
		static const UECodeGen_Private::FPropertyParamsBase* const PropPointers[];
		static const FCppClassTypeInfoStatic StaticCppClassTypeInfo;
		static const UECodeGen_Private::FClassParams ClassParams;
	};
	UObject* (*const Z_Construct_UClass_AGhost_Statics::DependentSingletons[])() = {
		(UObject* (*)())Z_Construct_UClass_AActor,
		(UObject* (*)())Z_Construct_UPackage__Script_learnUe,
	};
	const FClassFunctionLinkInfo Z_Construct_UClass_AGhost_Statics::FuncInfo[] = {
		{ &Z_Construct_UFunction_AGhost_PtLog, "PtLog" }, // 1286413536
	};
#if WITH_METADATA
	const UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams[] = {
		{ "IncludePath", "Ghost.h" },
		{ "ModuleRelativePath", "Ghost.h" },
	};
#endif
#if WITH_METADATA
	const UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_AGhost_Statics::NewProp_m_time_MetaData[] = {
		{ "Category", "Ghost" },
		{ "ModuleRelativePath", "Ghost.h" },
	};
#endif
	const UECodeGen_Private::FIntPropertyParams Z_Construct_UClass_AGhost_Statics::NewProp_m_time = { "m_time", nullptr, (EPropertyFlags)0x0010000000020001, UECodeGen_Private::EPropertyGenFlags::Int, RF_Public|RF_Transient|RF_MarkAsNative, 1, nullptr, nullptr, STRUCT_OFFSET(AGhost, m_time), METADATA_PARAMS(Z_Construct_UClass_AGhost_Statics::NewProp_m_time_MetaData, UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::NewProp_m_time_MetaData)) };
	const UECodeGen_Private::FPropertyParamsBase* const Z_Construct_UClass_AGhost_Statics::PropPointers[] = {
		(const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_AGhost_Statics::NewProp_m_time,
	};
	const FCppClassTypeInfoStatic Z_Construct_UClass_AGhost_Statics::StaticCppClassTypeInfo = {
		TCppClassTypeTraits<AGhost>::IsAbstract,
	};
	const UECodeGen_Private::FClassParams Z_Construct_UClass_AGhost_Statics::ClassParams = {
		&AGhost::StaticClass,
		"Ghost",
		&StaticCppClassTypeInfo,
		DependentSingletons,
		FuncInfo,
		Z_Construct_UClass_AGhost_Statics::PropPointers,
		nullptr,
		UE_ARRAY_COUNT(DependentSingletons),
		UE_ARRAY_COUNT(FuncInfo),
		UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::PropPointers),
		0,
		0x009000A4u,
		METADATA_PARAMS(Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams, UE_ARRAY_COUNT(Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams))
	};
	UClass* Z_Construct_UClass_AGhost()
	{
		if (!Z_Registration_Info_UClass_AGhost.OuterSingleton)
		{
			UECodeGen_Private::ConstructUClass(Z_Registration_Info_UClass_AGhost.OuterSingleton, Z_Construct_UClass_AGhost_Statics::ClassParams);
		}
		return Z_Registration_Info_UClass_AGhost.OuterSingleton;
	}
	template<> LEARNUE_API UClass* StaticClass<AGhost>()
	{
		return AGhost::StaticClass();
	}
	DEFINE_VTABLE_PTR_HELPER_CTOR(AGhost);
	struct Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics
	{
		static const FClassRegisterCompiledInInfo ClassInfo[];
	};
	const FClassRegisterCompiledInInfo Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo[] = {
		{ Z_Construct_UClass_AGhost, AGhost::StaticClass, TEXT("AGhost"), &Z_Registration_Info_UClass_AGhost, CONSTRUCT_RELOAD_VERSION_INFO(FClassReloadVersionInfo, sizeof(AGhost), 1620684469U) },
	};
	static FRegisterCompiledInInfo Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_3523060098(TEXT("/Script/learnUe"),
		Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo, UE_ARRAY_COUNT(Z_CompiledInDeferFile_FID_UeProject_learnUe_Source_learnUe_Ghost_h_Statics::ClassInfo),
		nullptr, 0,
		nullptr, 0);
PRAGMA_ENABLE_DEPRECATION_WARNINGS
相关推荐
JSU_曾是此间年少11 分钟前
数据结构——线性表与链表
数据结构·c++·算法
此生只爱蛋1 小时前
【手撕排序2】快速排序
c语言·c++·算法·排序算法
何曾参静谧1 小时前
「C/C++」C/C++ 指针篇 之 指针运算
c语言·开发语言·c++
lulu_gh_yu2 小时前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
ULTRA??3 小时前
C加加中的结构化绑定(解包,折叠展开)
开发语言·c++
凌云行者3 小时前
OpenGL入门005——使用Shader类管理着色器
c++·cmake·opengl
凌云行者3 小时前
OpenGL入门006——着色器在纹理混合中的应用
c++·cmake·opengl
~yY…s<#>4 小时前
【刷题17】最小栈、栈的压入弹出、逆波兰表达式
c语言·数据结构·c++·算法·leetcode
可均可可5 小时前
C++之OpenCV入门到提高004:Mat 对象的使用
c++·opencv·mat·imread·imwrite
白子寰5 小时前
【C++打怪之路Lv14】- “多态“篇
开发语言·c++