赞
踩
为了探寻UE4的反射机制,我们先从简单处着手,手动模拟一下generated.h的生成过程,看看UHT到底干了哪些事情。
以一个很简单的类作为分析对象
- UCLASS(meta=(IsBlueprintBase = "false"))
- class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
- {
-
- public:
- GENERATED_BODY()
-
- public:
- UFUNCTION(BlueprintCallable)
- void BlueprintCallableFunction();
-
- UFUNCTION(BlueprintPure)
- int BlueprintPureFunction();
-
- UFUNCTION(BlueprintNativeEvent)
- void BlueprintNativeEventFunction();
-
- void BlueprintNativeEventFunction_Implementation();
-
- public:
- UPROPERTY(BlueprintReadWrite)
- float PlayerHP = 2.0f;
- };
GENERATED_BODY()相关定义如下
- // This pair of macros is used to help implement GENERATED_BODY() and GENERATED_USTRUCT_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)
-
- // Include a redundant semicolon at the end of the generated code block, so that intellisense parsers can start parsing
- // a new declaration if the line number/generated code is out of date.
- #define GENERATED_BODY_LEGACY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY_LEGACY);
- #define GENERATED_BODY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY);
可以得出,GENERATED_BODY()被替换后,变成如下代码
- UCLASS(meta=(IsBlueprintBase = "false"))
- class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
- {
- CURRENT_FILE_ID_#__LINE__#_GENERATED_BODY
-
- ...
- };
其中__LINE__是需要被替换成GENERATED_BODY宏实际所在的行数,我们这里在15行,所以变成
- UCLASS(meta=(IsBlueprintBase = "false"))
- class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
- {
- CURRENT_FILE_ID_15_GENERATED_BODY
-
- ...
- };
CURRENT_FILE_ID是什么?秘密在RpgPlayer.generated.h里面,我们打开这个文件,可以看到头文件最后有
#define CURRENT_FILE_ID RpgGame_Source_RpgGame_RpgPlayer_h
将这个宏进行替换,于是变成
- UCLASS(meta=(IsBlueprintBase = "false"))
- class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
- {
- RpgGame_Source_RpgGame_RpgPlayer_h_15_GENERATED_BODY
-
- ...
- };
而RpgGame_Source_RpgGame_RpgPlayer_h_15_GENERATED_BODY又是什么东西呢?它也是一个宏!同样定义在RpgPlayer.generated.h里面,如下
- #define RpgGame_Source_RpgGame_RpgPlayer_h_15_GENERATED_BODY \
- PRAGMA_DISABLE_DEPRECATION_WARNINGS \
- public: \
- RpgGame_Source_RpgGame_RpgPlayer_h_15_PRIVATE_PROPERTY_OFFSET \
- RpgGame_Source_RpgGame_RpgPlayer_h_15_SPARSE_DATA \
- RpgGame_Source_RpgGame_RpgPlayer_h_15_RPC_WRAPPERS_NO_PURE_DECLS \
- RpgGame_Source_RpgGame_RpgPlayer_h_15_INCLASS_NO_PURE_DECLS \
- RpgGame_Source_RpgGame_RpgPlayer_h_15_ENHANCED_CONSTRUCTORS \
- private: \
- PRAGMA_ENABLE_DEPRECATION_WARNINGS
我们再进行替换
- UCLASS(meta=(IsBlueprintBase = "false"))
- class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
- {
- PRAGMA_DISABLE_DEPRECATION_WARNINGS
- public:
- RpgGame_Source_RpgGame_RpgPlayer_h_15_PRIVATE_PROPERTY_OFFSET
- RpgGame_Source_RpgGame_RpgPlayer_h_15_SPARSE_DATA
- RpgGame_Source_RpgGame_RpgPlayer_h_15_RPC_WRAPPERS_NO_PURE_DECLS
- RpgGame_Source_RpgGame_RpgPlayer_h_15_INCLASS_NO_PURE_DECLS
- RpgGame_Source_RpgGame_RpgPlayer_h_15_ENHANCED_CONSTRUCTORS
- private:
- PRAGMA_ENABLE_DEPRECATION_WARNINGS
-
- public:
- UFUNCTION(BlueprintCallable)
- void BlueprintCallableFunction();
-
- UFUNCTION(BlueprintPure)
- int BlueprintPureFunction();
-
- UFUNCTION(BlueprintNativeEvent)
- void BlueprintNativeEventFunction();
-
- void BlueprintNativeEventFunction_Implementation();
-
- public:
- UPROPERTY(BlueprintReadWrite)
- float PlayerHP = 2.0f;
- };
这些宏都可以在RpgPlayer.generated.h里面找到。
- #define RpgGame_Source_RpgGame_RpgPlayer_h_15_RPC_WRAPPERS_NO_PURE_DECLS \
- \
- DECLARE_FUNCTION(execBlueprintNativeEventFunction); \
- DECLARE_FUNCTION(execBlueprintPureFunction); \
- DECLARE_FUNCTION(execBlueprintCallableFunction);
这里有个问题,我们定义了5个函数,这个宏里面为什么只有3个exe函数?从规则上看,BlueprintCallable、BlueprintPure、BlueprintNativeEvent都进行了处理,而BlueprintImplementableEvent和一个原生的BlueprintNativeEventFunction_Implementation函数没有处理,这两个函数跑哪去了?我们查看gen.cpp文件,发现如下代码
- static FName NAME_ARpgPlayer_BlueprintImplementableEventFunction = FName(TEXT("BlueprintImplementableEventFunction"));
- void ARpgPlayer::BlueprintImplementableEventFunction()
- {
- ProcessEvent(FindFunctionChecked(NAME_ARpgPlayer_BlueprintImplementableEventFunction),NULL);
- }
- static FName NAME_ARpgPlayer_BlueprintNativeEventFunction = FName(TEXT("BlueprintNativeEventFunction"));
- void ARpgPlayer::BlueprintNativeEventFunction()
- {
- ProcessEvent(FindFunctionChecked(NAME_ARpgPlayer_BlueprintNativeEventFunction),NULL);
- }
可以看出,这两个函数被保留了!没有进行特殊处理。
- #define RpgGame_Source_RpgGame_RpgPlayer_h_15_INCLASS_NO_PURE_DECLS \
- private: \
- static void StaticRegisterNativesARpgPlayer(); \
- friend struct Z_Construct_UClass_ARpgPlayer_Statics; \
- public: \
- DECLARE_CLASS(ARpgPlayer, ARpgGameCharacter, COMPILED_IN_FLAGS(0 | CLASS_Config), CASTCLASS_None, TEXT("/Script/RpgGame"), NO_API) \
- DECLARE_SERIALIZER(ARpgPlayer)
- #define RpgGame_Source_RpgGame_RpgPlayer_h_15_ENHANCED_CONSTRUCTORS \
- /** Standard constructor, called after all reflected properties have been initialized */ \
- NO_API ARpgPlayer() { }; \
- private: \
- /** Private move- and copy-constructors, should never be used */ \
- NO_API ARpgPlayer(ARpgPlayer&&); \
- NO_API ARpgPlayer(const ARpgPlayer&); \
- public: \
- DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, ARpgPlayer); \
- DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(ARpgPlayer); \
- DEFINE_DEFAULT_CONSTRUCTOR_CALL(ARpgPlayer)
为什么有些宏的define是空的?可能是因为我们的测试用例非常简单,里面只有一个变量和几个简单的函数,所以很多数据都没有,还没有进行更复杂的测试。
将这一波宏替换后,得到如下代码
- UCLASS(meta=(IsBlueprintBase = "false"))
- class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
- {
- public:
- DECLARE_FUNCTION(execBlueprintNativeEventFunction);
- DECLARE_FUNCTION(execBlueprintPureFunction);
- DECLARE_FUNCTION(execBlueprintCallableFunction);
-
- void BlueprintNativeEventFunction();
- void BlueprintNativeEventFunction_Implementation();
-
- private:
- static void StaticRegisterNativesARpgPlayer();
- friend struct Z_Construct_UClass_ARpgPlayer_Statics;
- public:
- DECLARE_CLASS(ARpgPlayer, ARpgGameCharacter, COMPILED_IN_FLAGS(0 | CLASS_Config), CASTCLASS_None, TEXT("/Script/RpgGame"), NO_API)
- DECLARE_SERIALIZER(ARpgPlayer)
-
- /** Standard constructor, called after all reflected properties have been initialized */ \
- NO_API ARpgPlayer() { };
- private:
- /** Private move- and copy-constructors, should never be used */
- NO_API ARpgPlayer(ARpgPlayer&&);
- NO_API ARpgPlayer(const ARpgPlayer&);
- public:
- DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, ARpgPlayer);
- DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(ARpgPlayer);
- DEFINE_DEFAULT_CONSTRUCTOR_CALL(ARpgPlayer)
- private:
-
- public:
- UPROPERTY(BlueprintReadWrite)
- float PlayerHP = 2.0f;
- };
看起来代码清晰了一些,但还有些宏的存在,我们继续替换
- #define DECLARE_CLASS( TClass, TSuperClass, TStaticFlags, TStaticCastFlags, TPackage, TRequiredAPI ) \
- private: \
- TClass& operator=(TClass&&); \
- TClass& operator=(const TClass&); \
- TRequiredAPI static UClass* GetPrivateStaticClass(); \
- public: \
- /** Bitwise union of #EClassFlags pertaining to this class.*/ \
- enum {StaticClassFlags=TStaticFlags}; \
- /** Typedef for the base class ({{ typedef-type }}) */ \
- typedef TSuperClass Super;\
- /** Typedef for {{ typedef-type }}. */ \
- typedef TClass ThisClass;\
- /** Returns a UClass object representing this class at runtime */ \
- inline static UClass* StaticClass() \
- { \
- return GetPrivateStaticClass(); \
- } \
- /** Returns the package this class belongs in */ \
- inline static const TCHAR* StaticPackage() \
- { \
- return TPackage; \
- } \
- /** Returns the static cast flags for this class */ \
- inline static EClassCastFlags StaticClassCastFlags() \
- { \
- return TStaticCastFlags; \
- } \
- /** For internal use only; use StaticConstructObject() to create new objects. */ \
- inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags) \
- { \
- return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags); \
- } \
- /** For internal use only; use StaticConstructObject() to create new objects. */ \
- inline void* operator new( const size_t InSize, EInternal* InMem ) \
- { \
- return (void*)InMem; \
- }
DECLARE_SERIALIZER:同样定义在ObjectMacros.h中,
- #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; \
- }
DECLARE_FUNCTION:定义在ObjectMacros.h中。从注释中很容易看出来,这个宏很简单,没什么嵌套,就是包装了函数的声明。
- // This macro is used to declare a thunk function in autogenerated boilerplate code
- #define DECLARE_FUNCTION(func) static void func( UObject* Context, FFrame& Stack, RESULT_DECL )
我们把这几个宏替换掉,得到如下代码
- UCLASS(meta=(IsBlueprintBase = "false"))
- class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
- {
- public:
- static void execBlueprintNativeEventFunction( UObject* Context, FFrame& Stack, RESULT_DECL );
- static void execBlueprintPureFunction( UObject* Context, FFrame& Stack, RESULT_DECL );
- static void execBlueprintCallableFunction( UObject* Context, FFrame& Stack, RESULT_DECL );
-
- void BlueprintNativeEventFunction();
- void BlueprintNativeEventFunction_Implementation();
- private:
- static void StaticRegisterNativesARpgPlayer();
- friend struct Z_Construct_UClass_ARpgPlayer_Statics;
- public:
- private:
- ARpgPlayer& operator=(ARpgPlayer&&);
- ARpgPlayer& operator=(const ARpgPlayer&);
- NO_API static UClass* GetPrivateStaticClass();
- public:
- /** Bitwise union of #EClassFlags pertaining to this class.*/
- enum {StaticClassFlags=COMPILED_IN_FLAGS(0 | CLASS_Config)};
- /** Typedef for the base class ({{ typedef-type }}) */
- typedef ARpgGameCharacter Super;
- /** Typedef for {{ typedef-type }}. */
- typedef ARpgPlayer ThisClass;
- /** Returns a UClass object representing this class at runtime */
- inline static UClass* StaticClass()
- {
- return GetPrivateStaticClass();
- }
- /** Returns the package this class belongs in */
- inline static const TCHAR* StaticPackage()
- {
- return TEXT("/Script/RpgGame");
- }
- /** Returns the static cast flags for this class */
- inline static EClassCastFlags StaticClassCastFlags()
- {
- return CASTCLASS_None;
- }
- /** For internal use only; use StaticConstructObject() to create new objects. */
- inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags)
- {
- return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags);
- }
- /** For internal use only; use StaticConstructObject() to create new objects. */
- inline void* operator new( const size_t InSize, EInternal* InMem )
- {
- return (void*)InMem;
- }
-
- friend FArchive &operator<<( FArchive& Ar, ARpgPlayer*& Res )
- {
- return Ar << (UObject*&)Res;
- }
- friend void operator<<(FStructuredArchive::FSlot InSlot, ARpgPlayer*& Res)
- {
- InSlot << (UObject*&)Res;
- }
-
- /** Standard constructor, called after all reflected properties have been initialized */ \
- NO_API ARpgPlayer() { };
- private:
- /** Private move- and copy-constructors, should never be used */
- NO_API ARpgPlayer(ARpgPlayer&&);
- NO_API ARpgPlayer(const ARpgPlayer&);
- public:
- DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, ARpgPlayer);
- DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(ARpgPlayer);
- DEFINE_DEFAULT_CONSTRUCTOR_CALL(ARpgPlayer)
- private:
-
- public:
- UPROPERTY(BlueprintReadWrite)
- float PlayerHP = 2.0f;
- };
还有几个宏,我们继续探测
- #define DECLARE_VTABLE_PTR_HELPER_CTOR(API, TClass) \
- /** DO NOT USE. This constructor is for internal usage only for hot-reload purposes. */ \
- API TClass(FVTableHelper& Helper);
- #define DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER_DUMMY() \
- static UObject* __VTableCtorCaller(FVTableHelper& Helper) \
- { \
- return nullptr; \
- }
-
- #if WITH_HOT_RELOAD
- #define DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(TClass) \
- static UObject* __VTableCtorCaller(FVTableHelper& Helper) \
- { \
- return new (EC_InternalUseOnlyConstructor, (UObject*)GetTransientPackage(), NAME_None, RF_NeedLoad | RF_ClassDefaultObject | RF_TagGarbageTemp) TClass(Helper); \
- }
- #else // WITH_HOT_RELOAD
- #define DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(TClass) \
- DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER_DUMMY()
- #endif // WITH_HOT_RELOAD
- #define DEFINE_DEFAULT_CONSTRUCTOR_CALL(TClass) \
- static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass; }
UPROPERTY。可以看出这些宏只是辅助UHT扫描源码用的,实际没有任何有效的定义。
- // These macros wrap metadata parsed by the Unreal Header Tool, and are otherwise
- // ignored when code containing them is compiled by the C++ compiler
- #define UPROPERTY(...)
- #define UFUNCTION(...)
- #define USTRUCT(...)
- #define UMETA(...)
- #define UPARAM(...)
- #define UENUM(...)
- #define UDELEGATE(...)
- #define RIGVM_METHOD(...)
全部替换后,大致代码如下
- UCLASS(meta=(IsBlueprintBase = "false"))
- class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
- {
- public:
- static void execBlueprintNativeEventFunction( UObject* Context, FFrame& Stack, void*const Z_Param__Result);
- static void execBlueprintPureFunction( UObject* Context, FFrame& Stack, void*const Z_Param__Result);
- static void execBlueprintCallableFunction( UObject* Context, FFrame& Stack, void*const Z_Param__Result);
-
- void BlueprintNativeEventFunction();
- void BlueprintNativeEventFunction_Implementation();
- private:
- static void StaticRegisterNativesARpgPlayer();
- friend struct Z_Construct_UClass_ARpgPlayer_Statics; //定义在gen.cpp中
- public:
- private:
- ARpgPlayer& operator=(ARpgPlayer&&); //禁止移动赋值
- ARpgPlayer& operator=(const ARpgPlayer&); //禁止拷贝赋值
- NO_API static UClass* GetPrivateStaticClass();
- public:
- /** Bitwise union of #EClassFlags pertaining to this class.*/
- enum {StaticClassFlags=COMPILED_IN_FLAGS(0 | CLASS_Config)};
- /** Typedef for the base class ({{ typedef-type }}) */
- typedef ARpgGameCharacter Super;
- /** Typedef for {{ typedef-type }}. */
- typedef ARpgPlayer ThisClass;
- /** Returns a UClass object representing this class at runtime */
- inline static UClass* StaticClass()
- {
- return GetPrivateStaticClass();
- }
- /** Returns the package this class belongs in */
- inline static const TCHAR* StaticPackage()
- {
- return TEXT("/Script/RpgGame");
- }
- /** Returns the static cast flags for this class */
- inline static EClassCastFlags StaticClassCastFlags()
- {
- return CASTCLASS_None;
- }
- /** For internal use only; use StaticConstructObject() to create new objects. */
- inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags)
- {
- return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags);
- }
- /** For internal use only; use StaticConstructObject() to create new objects. */
- inline void* operator new( const size_t InSize, EInternal* InMem )
- {
- return (void*)InMem;
- }
-
- friend FArchive &operator<<( FArchive& Ar, ARpgPlayer*& Res )
- {
- return Ar << (UObject*&)Res;
- }
- friend void operator<<(FStructuredArchive::FSlot InSlot, ARpgPlayer*& Res)
- {
- InSlot << (UObject*&)Res;
- }
-
- /** Standard constructor, called after all reflected properties have been initialized */ \
- NO_API ARpgPlayer() { };
- private:
- /** Private move- and copy-constructors, should never be used */
- NO_API ARpgPlayer(ARpgPlayer&&);
- NO_API ARpgPlayer(const ARpgPlayer&);
- public:
- NO_API ARpgPlayer(FVTableHelper& Helper);
- static UObject* __VTableCtorCaller(FVTableHelper& Helper)
- {
- return nullptr;
- }
-
- static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())ARpgPlayer; }
- private:
-
- public:
- float PlayerHP = 2.0f;
- };
至此,我们generated.h生成过程大致模拟完毕,下一篇文章,我们将对替换后的代码进行简单的分析。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。