序
最近看了看反射相关的知识,用不说一点人话的方式来说,反射是程序在运行中能够动态获取修改或调用自身属性的东西。
一开始我是觉得反射用处好像不大,后续查了下一些反射的使用环境,发现我格局小了,我觉得用处不大的原因是我从c++的角度来考虑使用反射,在c++中确实用处不大,但是捏,把格局打开,要从更上一层的视角来看反射,不要局限于c++,比如我们要使用c++以外的东西调用c++,比如配置文件,蓝图,python,lua这些去调c++,或是设计到编辑器功能的时候,这时候反射就是极为好用方便的一个特性。
三种反射实现的方式
反射实现的本质其实就是想办法在运行时拿到类的数据,这个数据可以手动生成也可以利用编译器的信息,在c++中反射大概有三种实现方式
1.利用c++手动写反射结构的代码,可以利用宏之类的,其实就是方式3的简化版,实现简单相对简单,但是对使用者这来说毕竟麻烦,因为如果要添加一个新的成员或函数,就要手动添加一次。
2.利用c++编译出来的符号表,缺点每个编译器的实现不一定一样,换一个编译器可能就不好使了
3.利用工具生成c++代码实现反射,唯一缺点是实现起来比较有难度,但是使用起来简单,也不会有第二种方法的缺点
在ue中使用的就是第三种办法,所以ue实现了UHT这个工具。
UE5中反射系统注册信息的过程
UHT与宏
在编写ue代码时,相比大家一定遇到过类似于UCLASS,GENERATED_BODY,UFUNCTION这些宏,这些宏其实就是给UHT的一个标记,UHT更具这些宏生成generated.h和gen.cpp文件,这些宏在ObjectMarcos.h文件中可以找到详细的定义。现在我们来一步一步拨开这些宏背后所做的事情
比如,现在我们有这样一个AGhost类
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宏的定义
#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文件可以发现这样的一个宏
#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宏有着实际的替换内容。
#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文件中
#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
#define FID_UeProject_learnUe_Source_learnUe_Ghost_h_16_RPC_WRAPPERS \\DECLARE_FUNCTION(execPtLog);
我们同样可以在generated.h文件中找到这个宏的定义,发现是又套娃了一层DECLARE_FUNCTION的宏,宏的参数就是UFNCTION函数名,接着看看DECLARE_FUNCTION这个宏又做了什么。
#define DECLARE_FUNCTION(func) static void func( UObject* Context, FFrame& Stack, RESULT_DECL )
可以发现UFUNCTION宏会被UHT生成一个类内静态函数,这个类内静态函数的实现在对应的gen.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;}
把这段全是宏的代码展开
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
#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中
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宏看名字就很容易猜出来,必然是与序列化相关
#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
#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,需要注意的是,这个并不是定义了默认的构造函数
#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
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函数是比较长的,但是也比较简单
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_RELOADif (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);}}
#endifReturnClass = (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宏的函数,
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文件中有着这么一个静态变量
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封装好的一个,我们看下它的本质
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_RELOADif (result == FClassDeferredRegistry::AddResult::ExistingChanged && !IsReloadActive()){// Class exists, this can only happen during hot-reload or live codingUE_LOG(LogUObjectBase, Fatal, TEXT("Trying to recreate changed class '%s' outside of hot reload and live coding!"), InName);}
#endifFString 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的声明处。
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函数做了什么。
/// <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_RELOADconst 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);
#elseRegistrations.Add(FRegistrant{ InOuterRegister, InInnerRegister, InPackageName, &InInfo });return AddResult::New;
#endif}
AddRegistration函数的逻辑是现在自身map上找一下是否存在要add的类,如果有则看下是否为热重载,如果没有则加入map,最后在把信息包装成FRegistrant放入自身的Registrations数组中,待后续使用。
然后回到RegisterCompiledInInfo函数,在AddRegistration后又调用了NotifyRegistrationEvent函数,看名字是通知什么模块有注册事件。
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,来看下这个函数做了什么
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的函数。
void UClassRegisterAllCompiledInClasses()
{
#if WITH_RELOADTArray<UClass*> AddedClasses;
#endifSCOPED_BOOT_TIMING("UClassRegisterAllCompiledInClasses");FClassDeferredRegistry& Registry = FClassDeferredRegistry::Get();Registry.ProcessChangedObjects();for (const FClassDeferredRegistry::FRegistrant& Registrant : Registry.GetRegistrations()){UClass* RegisteredClass = FClassDeferredRegistry::InnerRegister(Registrant);
#if WITH_RELOADif (IsReloadActive() && Registrant.OldSingleton == nullptr){AddedClasses.Add(RegisteredClass);}
#endif}#if WITH_RELOADif (AddedClasses.Num() > 0){FCoreUObjectDelegates::ReloadAddedClassesDelegate.Broadcast(AddedClasses);PRAGMA_DISABLE_DEPRECATION_WARNINGSFCoreUObjectDelegates::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
。
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做了什么
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_METADATAAddMetaData(NewClass, Params.MetaDataArray, Params.NumMetaData);
#endifNewClass->StaticLink();NewClass->SetSparseClassDataStruct(NewClass->GetSparseClassDataArchetypeStruct());}
}
可以看到首先执行了parm.DependencySingletonFuncArray参数中所有的函数指针,而这个parm在gen.cpp文件是个叫ClassParams的FClassParmas的结构体,里面存放着UFuntion的信息。
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_METADATAconst FMetaDataPairParam* MetaDataArray;int32 NumMetaData;
#endif};
ConstructUClass函数主要就是利用FClassParams去构建Uclass的信息,如果全部展开篇幅实在是太太长了,我也写不动啦,想摸了。有兴趣可以自行找下生成的gen.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 ReferencesENGINE_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 ReferencesDEFINE_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_METADATAstatic const UECodeGen_Private::FMetaDataPairParam Function_MetaDataParams[];
#endifstatic 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_METADATAconst UECodeGen_Private::FMetaDataPairParam Z_Construct_UFunction_AGhost_PtLog_Statics::Function_MetaDataParams[] = {{ "ModuleRelativePath", "Ghost.h" },};
#endifconst 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_METADATAstatic const UECodeGen_Private::FMetaDataPairParam Class_MetaDataParams[];
#endif
#if WITH_METADATAstatic const UECodeGen_Private::FMetaDataPairParam NewProp_m_time_MetaData[];
#endifstatic 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_METADATAconst UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams[] = {{ "IncludePath", "Ghost.h" },{ "ModuleRelativePath", "Ghost.h" },};
#endif
#if WITH_METADATAconst UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_AGhost_Statics::NewProp_m_time_MetaData[] = {{ "Category", "Ghost" },{ "ModuleRelativePath", "Ghost.h" },};
#endifconst 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 ;
};
//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 ReferencesENGINE_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 ReferencesDEFINE_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_METADATAstatic const UECodeGen_Private::FMetaDataPairParam Function_MetaDataParams[];
#endifstatic 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_METADATAconst UECodeGen_Private::FMetaDataPairParam Z_Construct_UFunction_AGhost_PtLog_Statics::Function_MetaDataParams[] = {{ "ModuleRelativePath", "Ghost.h" },};
#endifconst 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_METADATAstatic const UECodeGen_Private::FMetaDataPairParam Class_MetaDataParams[];
#endif
#if WITH_METADATAstatic const UECodeGen_Private::FMetaDataPairParam NewProp_m_time_MetaData[];
#endifstatic 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_METADATAconst UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_AGhost_Statics::Class_MetaDataParams[] = {{ "IncludePath", "Ghost.h" },{ "ModuleRelativePath", "Ghost.h" },};
#endif
#if WITH_METADATAconst UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_AGhost_Statics::NewProp_m_time_MetaData[] = {{ "Category", "Ghost" },{ "ModuleRelativePath", "Ghost.h" },};
#endifconst 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