为了探寻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里面找到。
-
首先看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:定义如下 #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);
} 可以看出,这两个函数被保留了!没有进行特殊处理。 -
RpgGame_Source_RpgGame_RpgPlayer_h_15_CALLBACK_WRAPPERS:定义为了空。 -
RpgGame_Source_RpgGame_RpgPlayer_h_15_INCLASS_NO_PURE_DECLS:
#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)
- RpgGame_Source_RpgGame_RpgPlayer_h_15_ENHANCED_CONSTRUCTORS:
#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;
};
看起来代码清晰了一些,但还有些宏的存在,我们继续替换
- DECLARE_CLASS:这个宏定义在ObjectMacros.h中
#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_VTABLE_PTR_HELPER_CTOR_CALLER
#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_DEFAULT_CONSTRUCTOR_CALL
#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生成过程大致模拟完毕,下一篇文章,我们将对替换后的代码进行简单的分析。
|