序
最近看了看反射相关的知识,用不说一点人话的方式来说,反射是程序在运行中能够动态获取修改或调用自身属性的东西。
一开始我是觉得反射用处好像不大,后续查了下一些反射的使用环境,发现我格局小了,我觉得用处不大的原因是我从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