当前位置:   article > 正文

UE4 generated.h文件生成过程模拟

UE4 generated.h文件生成过程模拟

为了探寻UE4的反射机制,我们先从简单处着手,手动模拟一下generated.h的生成过程,看看UHT到底干了哪些事情。

以一个很简单的类作为分析对象

  1. UCLASS(meta=(IsBlueprintBase = "false"))
  2. class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
  3. {
  4. public:
  5. GENERATED_BODY()
  6. public:
  7. UFUNCTION(BlueprintCallable)
  8. void BlueprintCallableFunction();
  9. UFUNCTION(BlueprintPure)
  10. int BlueprintPureFunction();
  11. UFUNCTION(BlueprintNativeEvent)
  12. void BlueprintNativeEventFunction();
  13. void BlueprintNativeEventFunction_Implementation();
  14. public:
  15. UPROPERTY(BlueprintReadWrite)
  16. float PlayerHP = 2.0f;
  17. };

GENERATED_BODY()相关定义如下

  1. // This pair of macros is used to help implement GENERATED_BODY() and GENERATED_USTRUCT_BODY()
  2. #define BODY_MACRO_COMBINE_INNER(A,B,C,D) A##B##C##D
  3. #define BODY_MACRO_COMBINE(A,B,C,D) BODY_MACRO_COMBINE_INNER(A,B,C,D)
  4. // Include a redundant semicolon at the end of the generated code block, so that intellisense parsers can start parsing
  5. // a new declaration if the line number/generated code is out of date.
  6. #define GENERATED_BODY_LEGACY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY_LEGACY);
  7. #define GENERATED_BODY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY);

可以得出,GENERATED_BODY()被替换后,变成如下代码

  1. UCLASS(meta=(IsBlueprintBase = "false"))
  2. class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
  3. {
  4. CURRENT_FILE_ID_#__LINE__#_GENERATED_BODY
  5. ...
  6. };

其中__LINE__是需要被替换成GENERATED_BODY宏实际所在的行数,我们这里在15行,所以变成

  1. UCLASS(meta=(IsBlueprintBase = "false"))
  2. class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
  3. {
  4. CURRENT_FILE_ID_15_GENERATED_BODY
  5. ...
  6. };

CURRENT_FILE_ID是什么?秘密在RpgPlayer.generated.h里面,我们打开这个文件,可以看到头文件最后有

#define CURRENT_FILE_ID RpgGame_Source_RpgGame_RpgPlayer_h

将这个宏进行替换,于是变成

  1. UCLASS(meta=(IsBlueprintBase = "false"))
  2. class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
  3. {
  4. RpgGame_Source_RpgGame_RpgPlayer_h_15_GENERATED_BODY
  5. ...
  6. };

而RpgGame_Source_RpgGame_RpgPlayer_h_15_GENERATED_BODY又是什么东西呢?它也是一个宏!同样定义在RpgPlayer.generated.h里面,如下

  1. #define RpgGame_Source_RpgGame_RpgPlayer_h_15_GENERATED_BODY \
  2. PRAGMA_DISABLE_DEPRECATION_WARNINGS \
  3. public: \
  4. RpgGame_Source_RpgGame_RpgPlayer_h_15_PRIVATE_PROPERTY_OFFSET \
  5. RpgGame_Source_RpgGame_RpgPlayer_h_15_SPARSE_DATA \
  6. RpgGame_Source_RpgGame_RpgPlayer_h_15_RPC_WRAPPERS_NO_PURE_DECLS \
  7. RpgGame_Source_RpgGame_RpgPlayer_h_15_INCLASS_NO_PURE_DECLS \
  8. RpgGame_Source_RpgGame_RpgPlayer_h_15_ENHANCED_CONSTRUCTORS \
  9. private: \
  10. PRAGMA_ENABLE_DEPRECATION_WARNINGS

我们再进行替换

  1. UCLASS(meta=(IsBlueprintBase = "false"))
  2. class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
  3. {
  4. PRAGMA_DISABLE_DEPRECATION_WARNINGS
  5. public:
  6. RpgGame_Source_RpgGame_RpgPlayer_h_15_PRIVATE_PROPERTY_OFFSET
  7. RpgGame_Source_RpgGame_RpgPlayer_h_15_SPARSE_DATA
  8. RpgGame_Source_RpgGame_RpgPlayer_h_15_RPC_WRAPPERS_NO_PURE_DECLS
  9. RpgGame_Source_RpgGame_RpgPlayer_h_15_INCLASS_NO_PURE_DECLS
  10. RpgGame_Source_RpgGame_RpgPlayer_h_15_ENHANCED_CONSTRUCTORS
  11. private:
  12. PRAGMA_ENABLE_DEPRECATION_WARNINGS
  13. public:
  14. UFUNCTION(BlueprintCallable)
  15. void BlueprintCallableFunction();
  16. UFUNCTION(BlueprintPure)
  17. int BlueprintPureFunction();
  18. UFUNCTION(BlueprintNativeEvent)
  19. void BlueprintNativeEventFunction();
  20. void BlueprintNativeEventFunction_Implementation();
  21. public:
  22. UPROPERTY(BlueprintReadWrite)
  23. float PlayerHP = 2.0f;
  24. };

这些宏都可以在RpgPlayer.generated.h里面找到。

  • 首先看PRAGMA_DISABLE_DEPRECATION_WARNINGS:这个宏,我们可以在引擎找到好几个定义的地方,但大致做的事情都是同一个,就是消除掉一些编译警告,不同平台进行代码编译时,会报不同的警告,所以这个宏被不同的平台进行了定义,无关痛痒,因此跳过这个宏不谈。

  • RpgGame_Source_RpgGame_RpgPlayer_h_15_PRIVATE_PROPERTY_OFFSET :这个也在RpgPlayer.generated.h定义了,不过是空值。

  • RpgGame_Source_RpgGame_RpgPlayer_h_15_SPARSE_DATA:定义为了空值。

  • RpgGame_Source_RpgGame_RpgPlayer_h_15_RPC_WRAPPERS_NO_PURE_DECLS:定义如下

    1. #define RpgGame_Source_RpgGame_RpgPlayer_h_15_RPC_WRAPPERS_NO_PURE_DECLS \
    2. \
    3. DECLARE_FUNCTION(execBlueprintNativeEventFunction); \
    4. DECLARE_FUNCTION(execBlueprintPureFunction); \
    5. DECLARE_FUNCTION(execBlueprintCallableFunction);

    这里有个问题,我们定义了5个函数,这个宏里面为什么只有3个exe函数?从规则上看,BlueprintCallable、BlueprintPure、BlueprintNativeEvent都进行了处理,而BlueprintImplementableEvent和一个原生的BlueprintNativeEventFunction_Implementation函数没有处理,这两个函数跑哪去了?我们查看gen.cpp文件,发现如下代码

    1. static FName NAME_ARpgPlayer_BlueprintImplementableEventFunction = FName(TEXT("BlueprintImplementableEventFunction"));
    2. void ARpgPlayer::BlueprintImplementableEventFunction()
    3. {
    4. ProcessEvent(FindFunctionChecked(NAME_ARpgPlayer_BlueprintImplementableEventFunction),NULL);
    5. }
    6. static FName NAME_ARpgPlayer_BlueprintNativeEventFunction = FName(TEXT("BlueprintNativeEventFunction"));
    7. void ARpgPlayer::BlueprintNativeEventFunction()
    8. {
    9. ProcessEvent(FindFunctionChecked(NAME_ARpgPlayer_BlueprintNativeEventFunction),NULL);
    10. }

    可以看出,这两个函数被保留了!没有进行特殊处理。

  • RpgGame_Source_RpgGame_RpgPlayer_h_15_CALLBACK_WRAPPERS:定义为了空。

  • RpgGame_Source_RpgGame_RpgPlayer_h_15_INCLASS_NO_PURE_DECLS:

  1. #define RpgGame_Source_RpgGame_RpgPlayer_h_15_INCLASS_NO_PURE_DECLS \
  2. private: \
  3. static void StaticRegisterNativesARpgPlayer(); \
  4. friend struct Z_Construct_UClass_ARpgPlayer_Statics; \
  5. public: \
  6. DECLARE_CLASS(ARpgPlayer, ARpgGameCharacter, COMPILED_IN_FLAGS(0 | CLASS_Config), CASTCLASS_None, TEXT("/Script/RpgGame"), NO_API) \
  7. DECLARE_SERIALIZER(ARpgPlayer)
  • RpgGame_Source_RpgGame_RpgPlayer_h_15_ENHANCED_CONSTRUCTORS:
    1. #define RpgGame_Source_RpgGame_RpgPlayer_h_15_ENHANCED_CONSTRUCTORS \
    2. /** Standard constructor, called after all reflected properties have been initialized */ \
    3. NO_API ARpgPlayer() { }; \
    4. private: \
    5. /** Private move- and copy-constructors, should never be used */ \
    6. NO_API ARpgPlayer(ARpgPlayer&&); \
    7. NO_API ARpgPlayer(const ARpgPlayer&); \
    8. public: \
    9. DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, ARpgPlayer); \
    10. DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(ARpgPlayer); \
    11. DEFINE_DEFAULT_CONSTRUCTOR_CALL(ARpgPlayer)

    为什么有些宏的define是空的?可能是因为我们的测试用例非常简单,里面只有一个变量和几个简单的函数,所以很多数据都没有,还没有进行更复杂的测试。

将这一波宏替换后,得到如下代码

  1. UCLASS(meta=(IsBlueprintBase = "false"))
  2. class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
  3. {
  4. public:
  5. DECLARE_FUNCTION(execBlueprintNativeEventFunction);
  6. DECLARE_FUNCTION(execBlueprintPureFunction);
  7. DECLARE_FUNCTION(execBlueprintCallableFunction);
  8. void BlueprintNativeEventFunction();
  9. void BlueprintNativeEventFunction_Implementation();
  10. private:
  11. static void StaticRegisterNativesARpgPlayer();
  12. friend struct Z_Construct_UClass_ARpgPlayer_Statics;
  13. public:
  14. DECLARE_CLASS(ARpgPlayer, ARpgGameCharacter, COMPILED_IN_FLAGS(0 | CLASS_Config), CASTCLASS_None, TEXT("/Script/RpgGame"), NO_API)
  15. DECLARE_SERIALIZER(ARpgPlayer)
  16. /** Standard constructor, called after all reflected properties have been initialized */ \
  17. NO_API ARpgPlayer() { };
  18. private:
  19. /** Private move- and copy-constructors, should never be used */
  20. NO_API ARpgPlayer(ARpgPlayer&&);
  21. NO_API ARpgPlayer(const ARpgPlayer&);
  22. public:
  23. DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, ARpgPlayer);
  24. DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(ARpgPlayer);
  25. DEFINE_DEFAULT_CONSTRUCTOR_CALL(ARpgPlayer)
  26. private:
  27. public:
  28. UPROPERTY(BlueprintReadWrite)
  29. float PlayerHP = 2.0f;
  30. };

看起来代码清晰了一些,但还有些宏的存在,我们继续替换

  • DECLARE_CLASS:这个宏定义在ObjectMacros.h中
    1. #define DECLARE_CLASS( TClass, TSuperClass, TStaticFlags, TStaticCastFlags, TPackage, TRequiredAPI ) \
    2. private: \
    3. TClass& operator=(TClass&&); \
    4. TClass& operator=(const TClass&); \
    5. TRequiredAPI static UClass* GetPrivateStaticClass(); \
    6. public: \
    7. /** Bitwise union of #EClassFlags pertaining to this class.*/ \
    8. enum {StaticClassFlags=TStaticFlags}; \
    9. /** Typedef for the base class ({{ typedef-type }}) */ \
    10. typedef TSuperClass Super;\
    11. /** Typedef for {{ typedef-type }}. */ \
    12. typedef TClass ThisClass;\
    13. /** Returns a UClass object representing this class at runtime */ \
    14. inline static UClass* StaticClass() \
    15. { \
    16. return GetPrivateStaticClass(); \
    17. } \
    18. /** Returns the package this class belongs in */ \
    19. inline static const TCHAR* StaticPackage() \
    20. { \
    21. return TPackage; \
    22. } \
    23. /** Returns the static cast flags for this class */ \
    24. inline static EClassCastFlags StaticClassCastFlags() \
    25. { \
    26. return TStaticCastFlags; \
    27. } \
    28. /** For internal use only; use StaticConstructObject() to create new objects. */ \
    29. inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags) \
    30. { \
    31. return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags); \
    32. } \
    33. /** For internal use only; use StaticConstructObject() to create new objects. */ \
    34. inline void* operator new( const size_t InSize, EInternal* InMem ) \
    35. { \
    36. return (void*)InMem; \
    37. }

    DECLARE_SERIALIZER:同样定义在ObjectMacros.h中,

    1. #define DECLARE_SERIALIZER( TClass ) \
    2. friend FArchive &operator<<( FArchive& Ar, TClass*& Res ) \
    3. { \
    4. return Ar << (UObject*&)Res; \
    5. } \
    6. friend void operator<<(FStructuredArchive::FSlot InSlot, TClass*& Res) \
    7. { \
    8. InSlot << (UObject*&)Res; \
    9. }

    DECLARE_FUNCTION:定义在ObjectMacros.h中。从注释中很容易看出来,这个宏很简单,没什么嵌套,就是包装了函数的声明。

    1. // This macro is used to declare a thunk function in autogenerated boilerplate code
    2. #define DECLARE_FUNCTION(func) static void func( UObject* Context, FFrame& Stack, RESULT_DECL )

  • 我们把这几个宏替换掉,得到如下代码

    1. UCLASS(meta=(IsBlueprintBase = "false"))
    2. class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
    3. {
    4. public:
    5. static void execBlueprintNativeEventFunction( UObject* Context, FFrame& Stack, RESULT_DECL );
    6. static void execBlueprintPureFunction( UObject* Context, FFrame& Stack, RESULT_DECL );
    7. static void execBlueprintCallableFunction( UObject* Context, FFrame& Stack, RESULT_DECL );
    8. void BlueprintNativeEventFunction();
    9. void BlueprintNativeEventFunction_Implementation();
    10. private:
    11. static void StaticRegisterNativesARpgPlayer();
    12. friend struct Z_Construct_UClass_ARpgPlayer_Statics;
    13. public:
    14. private:
    15. ARpgPlayer& operator=(ARpgPlayer&&);
    16. ARpgPlayer& operator=(const ARpgPlayer&);
    17. NO_API static UClass* GetPrivateStaticClass();
    18. public:
    19. /** Bitwise union of #EClassFlags pertaining to this class.*/
    20. enum {StaticClassFlags=COMPILED_IN_FLAGS(0 | CLASS_Config)};
    21. /** Typedef for the base class ({{ typedef-type }}) */
    22. typedef ARpgGameCharacter Super;
    23. /** Typedef for {{ typedef-type }}. */
    24. typedef ARpgPlayer ThisClass;
    25. /** Returns a UClass object representing this class at runtime */
    26. inline static UClass* StaticClass()
    27. {
    28. return GetPrivateStaticClass();
    29. }
    30. /** Returns the package this class belongs in */
    31. inline static const TCHAR* StaticPackage()
    32. {
    33. return TEXT("/Script/RpgGame");
    34. }
    35. /** Returns the static cast flags for this class */
    36. inline static EClassCastFlags StaticClassCastFlags()
    37. {
    38. return CASTCLASS_None;
    39. }
    40. /** For internal use only; use StaticConstructObject() to create new objects. */
    41. inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags)
    42. {
    43. return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags);
    44. }
    45. /** For internal use only; use StaticConstructObject() to create new objects. */
    46. inline void* operator new( const size_t InSize, EInternal* InMem )
    47. {
    48. return (void*)InMem;
    49. }
    50. friend FArchive &operator<<( FArchive& Ar, ARpgPlayer*& Res )
    51. {
    52. return Ar << (UObject*&)Res;
    53. }
    54. friend void operator<<(FStructuredArchive::FSlot InSlot, ARpgPlayer*& Res)
    55. {
    56. InSlot << (UObject*&)Res;
    57. }
    58. /** Standard constructor, called after all reflected properties have been initialized */ \
    59. NO_API ARpgPlayer() { };
    60. private:
    61. /** Private move- and copy-constructors, should never be used */
    62. NO_API ARpgPlayer(ARpgPlayer&&);
    63. NO_API ARpgPlayer(const ARpgPlayer&);
    64. public:
    65. DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, ARpgPlayer);
    66. DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(ARpgPlayer);
    67. DEFINE_DEFAULT_CONSTRUCTOR_CALL(ARpgPlayer)
    68. private:
    69. public:
    70. UPROPERTY(BlueprintReadWrite)
    71. float PlayerHP = 2.0f;
    72. };

    还有几个宏,我们继续探测

    1. #define DECLARE_VTABLE_PTR_HELPER_CTOR(API, TClass) \
    2. /** DO NOT USE. This constructor is for internal usage only for hot-reload purposes. */ \
    3. API TClass(FVTableHelper& Helper);
  •  DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER
  1. #define DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER_DUMMY() \
  2. static UObject* __VTableCtorCaller(FVTableHelper& Helper) \
  3. { \
  4. return nullptr; \
  5. }
  6. #if WITH_HOT_RELOAD
  7. #define DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(TClass) \
  8. static UObject* __VTableCtorCaller(FVTableHelper& Helper) \
  9. { \
  10. return new (EC_InternalUseOnlyConstructor, (UObject*)GetTransientPackage(), NAME_None, RF_NeedLoad | RF_ClassDefaultObject | RF_TagGarbageTemp) TClass(Helper); \
  11. }
  12. #else // WITH_HOT_RELOAD
  13. #define DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(TClass) \
  14. DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER_DUMMY()
  15. #endif // WITH_HOT_RELOAD
  • DEFINE_DEFAULT_CONSTRUCTOR_CALL
    1. #define DEFINE_DEFAULT_CONSTRUCTOR_CALL(TClass) \
    2. static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass; }
  • UPROPERTY。可以看出这些宏只是辅助UHT扫描源码用的,实际没有任何有效的定义。

    1. // These macros wrap metadata parsed by the Unreal Header Tool, and are otherwise
    2. // ignored when code containing them is compiled by the C++ compiler
    3. #define UPROPERTY(...)
    4. #define UFUNCTION(...)
    5. #define USTRUCT(...)
    6. #define UMETA(...)
    7. #define UPARAM(...)
    8. #define UENUM(...)
    9. #define UDELEGATE(...)
    10. #define RIGVM_METHOD(...)

    全部替换后,大致代码如下

  1. UCLASS(meta=(IsBlueprintBase = "false"))
  2. class RPGGAME_API ARpgPlayer : public ARpgGameCharacter
  3. {
  4. public:
  5. static void execBlueprintNativeEventFunction( UObject* Context, FFrame& Stack, void*const Z_Param__Result);
  6. static void execBlueprintPureFunction( UObject* Context, FFrame& Stack, void*const Z_Param__Result);
  7. static void execBlueprintCallableFunction( UObject* Context, FFrame& Stack, void*const Z_Param__Result);
  8. void BlueprintNativeEventFunction();
  9. void BlueprintNativeEventFunction_Implementation();
  10. private:
  11. static void StaticRegisterNativesARpgPlayer();
  12. friend struct Z_Construct_UClass_ARpgPlayer_Statics; //定义在gen.cpp中
  13. public:
  14. private:
  15. ARpgPlayer& operator=(ARpgPlayer&&); //禁止移动赋值
  16. ARpgPlayer& operator=(const ARpgPlayer&); //禁止拷贝赋值
  17. NO_API static UClass* GetPrivateStaticClass();
  18. public:
  19. /** Bitwise union of #EClassFlags pertaining to this class.*/
  20. enum {StaticClassFlags=COMPILED_IN_FLAGS(0 | CLASS_Config)};
  21. /** Typedef for the base class ({{ typedef-type }}) */
  22. typedef ARpgGameCharacter Super;
  23. /** Typedef for {{ typedef-type }}. */
  24. typedef ARpgPlayer ThisClass;
  25. /** Returns a UClass object representing this class at runtime */
  26. inline static UClass* StaticClass()
  27. {
  28. return GetPrivateStaticClass();
  29. }
  30. /** Returns the package this class belongs in */
  31. inline static const TCHAR* StaticPackage()
  32. {
  33. return TEXT("/Script/RpgGame");
  34. }
  35. /** Returns the static cast flags for this class */
  36. inline static EClassCastFlags StaticClassCastFlags()
  37. {
  38. return CASTCLASS_None;
  39. }
  40. /** For internal use only; use StaticConstructObject() to create new objects. */
  41. inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags)
  42. {
  43. return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags);
  44. }
  45. /** For internal use only; use StaticConstructObject() to create new objects. */
  46. inline void* operator new( const size_t InSize, EInternal* InMem )
  47. {
  48. return (void*)InMem;
  49. }
  50. friend FArchive &operator<<( FArchive& Ar, ARpgPlayer*& Res )
  51. {
  52. return Ar << (UObject*&)Res;
  53. }
  54. friend void operator<<(FStructuredArchive::FSlot InSlot, ARpgPlayer*& Res)
  55. {
  56. InSlot << (UObject*&)Res;
  57. }
  58. /** Standard constructor, called after all reflected properties have been initialized */ \
  59. NO_API ARpgPlayer() { };
  60. private:
  61. /** Private move- and copy-constructors, should never be used */
  62. NO_API ARpgPlayer(ARpgPlayer&&);
  63. NO_API ARpgPlayer(const ARpgPlayer&);
  64. public:
  65. NO_API ARpgPlayer(FVTableHelper& Helper);
  66. static UObject* __VTableCtorCaller(FVTableHelper& Helper)
  67. {
  68. return nullptr;
  69. }
  70. static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())ARpgPlayer; }
  71. private:
  72. public:
  73. float PlayerHP = 2.0f;
  74. };

至此,我们generated.h生成过程大致模拟完毕,下一篇文章,我们将对替换后的代码进行简单的分析。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/275468
推荐阅读
  

闽ICP备14008679号