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

Redis缓存预热
说明:项目中使用到Redis,正常情况,我们会在用户首次查询数据的同时把该数据按照一定命名规则,存储到Redis中,称为冷启动(如下图),这种方式在一些情况下可能会给数据库带来较大的压力…...

Android 耗时分析(adb shell/Studio CPU Profiler/插桩Trace API)
1.adb logcat 查看冷启动时间和Activity显示时间: 过滤Displayed关键字,可看到Activity的显示时间 那上面display后面的是时间是指包含哪些过程的时间呢? 模拟在Application中沉睡1秒操作,冷启动情况下: 从上可知&…...
保护隐私与安全的防关联、多开浏览器
随着互联网的不断发展,我们越来越离不开浏览器这个工具,它为我们提供了便捷的网络浏览体验。然而,随着我们在互联网上的活动越来越多,我们的个人信息和隐私也日益暴露在网络风险之下。在这种背景下,为了保护个人隐私和…...

CloudStudio搭建Next框架博客_抛开电脑性能在云端编程(沉浸式体验)
文章目录 ⭐前言⭐进入cloud studio工作区指引💖 注册coding账号💖 选择cloud studio💖 cloud studio选择next.js💖 安装react的ui框架(tDesign)💖 安装axios💖 代理请求跨域&#x…...
【FPGA IP系列】FIFO深度计算详解
FIFO(First In First Out)是一种先进先出的存储结构,经常被用来在FPGA设计中进行数据缓存或者匹配传输速率。 FIFO的一个关键参数是其深度,也就是FIFO能够存储的数据条数,深度设计的合理,可以防止数据溢出,也可以节省…...
JavaScript中语句和表达式
在JavaScript编程中,Statements和Expressions都是代码的构建块,但它们有不同的特点和用途。 ● Statements(语句)是执行某些操作的完整命令;每个语句通常以分号结束。例如,if语句、for语句、switch语句、函…...
打卡力扣题目十
#左耳听风 ARST 打卡活动重启# 目录 一、题目 二、解决方法一 三、解决方法二 关于 ARTS 的释义 —— 每周完成一个 ARTS: ● Algorithm: 每周至少做一个 LeetCode 的算法题 ● Review: 阅读并点评至少一篇英文技术文章 ● Tips: 学习至少一个技术技巧 ● Shar…...
UniApp实现API接口封装与请求方法的设计与开发方法
UniApp实现API接口封装与请求方法的设计与开发方法 导语:UniApp是一个基于Vue.js的跨平台开发框架,可以同时开发iOS、Android和H5应用。在UniApp中,实现API接口封装与请求方法的设计与开发是一个十分重要的部分。本文将介绍如何使用UniApp实…...

利用小波分解信号,再重构
function [ output_args ] example4_5( input_args ) %EXAMPLE4_5 Summary of this function goes here % Detailed explanation goes here clc; clear; load leleccum; s leleccum(1:3920); % 进行3层小波分解,小波基函数为db2 [c,l] wavedec(s,3,db2); %进行…...

QT数据库编程
ui界面 mainwindow.cpp #include "mainwindow.h" #include "ui_mainwindow.h" #include <QButtonGroup> #include <QFileDialog> #include <QMessageBox> MainWindow::MainWindow(QWidget* parent): QMainWindow(parent), ui(new Ui::M…...

基于stm32单片机的直流电机速度控制——LZW
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 一、实验目的二、实验方法三、实验设计1.实验器材2.电路连接3.软件设计(1)实验变量(2)功能模块a)电机接收信号…...
实际项目中使用mockjs模拟数据
项目中的痛点 自己模拟的数据对代码的侵入程度太高,接口完成后要删掉对应的代码,导致接口开发完后端同事开发完,前端自己得加班;接口联调的时间有可能会延期,接口完成的质量参差不齐;对于数据量过大的模拟…...

【家庭公网IPv6】
家庭公网IPv6 这里有两个网站: 1、 IPV6版、多地Tcping、禁Ping版、tcp协议、tcping、端口延迟测试,在本机搭建好服务器后,可以用这个测试外网是否可以访问本机; 2、 IP查询ipw.cn,这个可以查询本机的网络是否IPv6访问…...

【iOS】Frame与Bounds的区别详解
iOS的坐标系 iOS特有的坐标是,是在iOS坐标系的左上角为坐标原点,往右为X正方向,向下为Y正方向。 bounds和frame都是属于CGRect类型的结构体,系统的定义如下,包含一个CGPoint(起点)和一个CGSiz…...

SpringBoot百货超市商城系统 附带详细运行指导视频
文章目录 一、项目演示二、项目介绍三、运行截图四、主要代码 一、项目演示 项目演示地址: 视频地址 二、项目介绍 项目描述:这是一个基于SpringBoot框架开发的百货超市系统。首先,这是一个很适合SpringBoot初学者学习的项目,代…...

【实践篇】推荐算法PaaS化探索与实践 | 京东云技术团队
作者:京东零售 崔宁 1. 背景说明 目前,推荐算法部支持了主站、企业业务、全渠道等20业务线的900推荐场景,通过梳理大促运营、各垂直业务线推荐场景的共性需求,对现有推荐算法能力进行沉淀和积累,并通过算法PaaS化打造…...

持续贡献开源力量,棱镜七彩加入openKylin
近日,棱镜七彩签署 openKylin 社区 CLA(Contributor License Agreement 贡献者许可协议),正式加入openKylin 开源社区。 棱镜七彩成立于2016年,是一家专注于开源安全、软件供应链安全的创新型科技企业。自成立以来&…...
Kafka的消费者如何管理偏移量?
在Kafka中,消费者可以通过管理和跟踪偏移量(offset)来确保消费者在消费消息时的准确性和可靠性。偏移量表示消费者在特定分区中已经消费的消息的位置。以下是几种常见的偏移量管理方式: 手动提交偏移量:消费者可以通过…...

IntelliJ IDEA流行的构建工具——Gradle
IntelliJ IDEA,是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的java开发工具,尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能可以说是超常的。 如…...

nacos源码打包及相关配置
nacos 本地下载后,需要 install 下: mvn clean install -Dmaven.test.skiptrue -Dcheckstyle.skiptrue -Dpmd.skiptrue -Drat.skiptruenacos源码修改后,重新打包生成压缩包命令:在 distribution 目录中运行: mvn -Pr…...

【机器学习】Multiple Variable Linear Regression
Multiple Variable Linear Regression 1、问题描述1.1 包含样例的X矩阵1.2 参数向量 w, b 2、多变量的模型预测2.1 逐元素进行预测2.2 向量点积进行预测 3、多变量线性回归模型计算损失4、多变量线性回归模型梯度下降4.1 计算梯度4.2梯度下降 首先,导入所需的库 im…...

自己创建的类,其他类中使用错误
说明:自己创建的类,在其他类中创建,报下面的错误(Cannot resolve sysmbol ‘Redishandler’); 解决:看下是不是漏掉了包名 加上包名,问题解决;...

Packet Tracer – 使用 TFTP 服务器升级思科 IOS 映像。
Packet Tracer – 使用 TFTP 服务器升级思科 IOS 映像。 地址分配表 设备 接口 IP 地址 子网掩码 默认网关 R1 F0/0 192.168.2.1 255.255.255.0 不适用 R2 G0/0 192.168.2.2 255.255.255.0 不适用 S1 VLAN 1 192.168.2.3 255.255.255.0 192.168.2.1 TFTP …...

并查集基础
一、概念及其介绍 并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。 并查集的思想是用一个数组表示了整片森林(parent),树的根节点唯一标识了一个集合,我们只要找到了某个元素的的树根…...
C# 循环等知识点
《1》程序:事先写好的指令(代码) using 准备工具 namespace 模块名称 { class 子模块{ static void main()//具体事项 { 代码 } } } 《2》变量:内存里的一块空间,用来存储数据常用的有小数,整数,…...

1.1.2 SpringCloud 版本问题
目录 版本标识 版本类型 查看对应版本 版本兼容的权威——官网: 具体的版本匹配支持信息可以查看 总结 在将Spring Cloud集成到Spring Boot项目中时,确保选择正确的Spring Cloud版本和兼容性是非常重要的。由于Spring Cloud存在多个版本,因此…...

Android AIDL 使用
工程目录图 请点击下面工程名称,跳转到代码的仓库页面,将工程 下载下来 Demo Code 里有详细的注释 代码:LearnAIDL代码:AIDLClient. 参考文献 安卓开发学习之AIDL的使用android进阶-AIDL的基本使用Android AIDL 使用使用 AIDL …...
MongoDB——命令详解
db.fruit.remove({name:apple})//删除a为apple的记录db.fruit.remove({})//删除所有的记录db.fruit.remove()//报错 MongoDB使用及命令大全(一)_mongodb 删除命令_言不及行yyds的博客-CSDN博客...

机器学习深度学习——多层感知机的简洁实现
👨🎓作者简介:一位即将上大四,正专攻机器学习的保研er 🌌上期文章:机器学习&&深度学习——多层感知机的从零开始实现 📚订阅专栏:机器学习&&深度学习 希望文章对你…...