
UHT가 타입 메타데이터 정보를 충분히 분석하고 획득했다면, 다음 단계는 이 정보를 활용해 앞서 설명한 타입 시스템 구조를 프로그램의 메모리 내에 실제로 구성하는 것입니다. 이 절차적 단계는 "등록(registration)"이라 하며, 이후 별도의 섹션에서 더 자세히 설명합니다.
핵심 개념:
일반적인 프로그램의 빌드 과정이 전처리, 컴파일, 어셈블, 링킹 등 여러 단계를 거치는 것처럼, 언리얼 엔진도 메모리 내에서의 타입 시스템 구축 과정을 다음과 같이 개념화합니다: 생성(generation), 수집(collection), 등록(registration), 그리고 연결(linking).
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "MyClass.generated.h"
/**
*
*/
UCLASS()
class REFLECTIONTEST_API UMyClass : public UObject
{
GENERATED_BODY()
public:
UPROPERTY()
int Health;
UFUNCTION(BlueprintCallable, Category = "Hello")
void CallableFunc();
UFUNCTION(BlueprintNativeEvent, Category = "Hello")
void NativeFunc();
UFUNCTION(BlueprintImplementableEvent, Category = "Hello")
void ImplementableFunc();
};
UENUM()
enum EMyEnum : int
{
E_Num1,
E_Num2,
E_Num3,
};
USTRUCT()
struct REFLECTIONTEST_API FMyStruct
{
GENERATED_USTRUCT_BODY();
UPROPERTY()
float Score;
};
UINTERFACE(BlueprintType)
class REFLECTIONTEST_API UMyInterface : public UInterface
{
GENERATED_UINTERFACE_BODY()
};
class IMyInterface
{
GENERATED_IINTERFACE_BODY()
public:
UFUNCTION(BlueprintImplementableEvent)
void BPFunc() const;
};
GENERATED_BODY() - 이 매크로는 메타데이터 정의와 함께 주요 선언부를 생성하며, ObjectMacros.h 689번째 줄(UE 5.2 기준)에서 확인할 수 있습니다.
// GENERATED_BODY()와 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)
// 생성된 코드 블록 끝에 불필요한 세미콜론을 포함하여,
// intellisense 파서가 줄 번호/생성 코드가 오래된 경우에도 새 선언을 파싱할 수 있도록 함
#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)
BODY_MACRO_COMBINE 매크로란?
BODY_MACRO_COMBINE_INNER는 전달된 매개변수들을 하나의 문자열로 이어붙이는 역할을 합니다. 예를 들어,
FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_13_PROLOG 와 같은 결과가 생성됩니다.
형식은 CURRENT_FILE_ID_12_PROLOG와 같으며, 여기서 12는 UCLASS가 작성된 실제 파일의 줄 번호입니다.
즉, CURRENT_FILE_ID는 BODY_MACRO_COMBINE이 호출되기 전에 치환됩니다.
매크로의 메타데이터 파라미터에서 키워드와 지정자에 대한 세부 설명은 생략합니다.
시작 부분에는 #pragma once가 정의되지 않은 경우를 대비해 MyClass.generated.h가 중복 포함되는 것을 방지하는 #ifdef 전처리 지시문이 있습니다.
PRAGMA_DISABLE_DEPRECATION_WARNINGS
#ifdef REFLECTIONTEST_MyClass_generated_h
#error "MyClass.generated.h already included, missing '#pragma once' in MyClass.h"
#endif
#define REFLECTIONTEST_MyClass_generated_h
#define FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_ACCESSORS
#define FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_CALLBACK_WRAPPERS
#define FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_INCLASS_NO_PURE_DECLS
private:
static void StaticRegisterNativesUMyClass();
friend struct Z_Construct_UClass_UMyClass_Statics;
public:
DECLARE_CLASS(UMyClass, UObject, COMPILED_IN_FLAGS(0), CASTCLASS_None, TEXT("/Script/ReflectionTest"), NO_API)
DECLARE_SERIALIZER(UMyClass)
#define FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_INCLASS
private:
static void StaticRegisterNativesUMyClass();
friend struct Z_Construct_UClass_UMyClass_Statics;
public:
DECLARE_CLASS(UMyClass, UObject, COMPILED_IN_FLAGS(0), CASTCLASS_None, TEXT("/Script/ReflectionTest"), NO_API)
DECLARE_SERIALIZER(UMyClass)
#define FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_STANDARD_CONSTRUCTORS
/** Standard constructor, called after all reflected properties have been initialised */
NO_API UMyClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyClass)
DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, UMyClass);
DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyClass);
private:
/** Private move- and copy-constructors, should never be used */
NO_API UMyClass(UMyClass&&);
NO_API UMyClass(const UMyClass&);
public:
NO_API virtual ~UMyClass();
#define FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_ENHANCED_CONSTRUCTORS
/** Standard constructor, called after all reflected properties have been initialised */
NO_API UMyClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
private:
/** Private move- and copy-constructors, should never be used */
NO_API UMyClass(UMyClass&&);
NO_API UMyClass(const UMyClass&);
public:
DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, UMyClass);
DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(UMyClass);
DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UMyClass)
NO_API virtual ~UMyClass();
#define FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_28_PROLOG
#define FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_GENERATED_BODY_LEGACY
PRAGMA_DISABLE_DEPRECATION_WARNINGS
public:
FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_SPARSE_DATA
FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_RPC_WRAPPERS
FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_ACCESSORS
FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_CALLBACK_WRAPPERS
FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_INCLASS
FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_STANDARD_CONSTRUCTORS
public:
PRAGMA_ENABLE_DEPRECATION_WARNINGS
#define FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_GENERATED_BODY
PRAGMA_DISABLE_DEPRECATION_WARNINGS
public:
FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_SPARSE_DATA
FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_RPC_WRAPPERS_NO_PURE_DECLS
FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_ACCESSORS
FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_CALLBACK_WRAPPERS
FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_INCLASS_NO_PURE_DECLS
FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_31_ENHANCED_CONSTRUCTORS
private:
PRAGMA_ENABLE_DEPRECATION_WARNINGS
CTRL+F Definitions:
INCLASS_DECLS
INCLASS_NO_PURE_DECLS
#define DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(TClass)
static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass(X);
DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(TClass)
이 매크로는 매우 중요합니다. C++에서는 네이티브 C++ 생성자에 직접 함수 포인터를 지정할 수 없기 때문입니다. 언리얼 엔진은 이 한계를 해결하기 위해 일반 함수 포인터로 생성자 래퍼를 도입합니다. 모든 생성자는 동일한 시그니처를 가지며, UClass 내부의 함수 포인터에 저장됩니다.
DECLARE_CLASS
DECLARE_CLASS(UMyClass, UObject, COMPILED_IN_FLAGS(0), CASTCLASS_None, TEXT("/Script/ReflectionTest"), NO_API)
DECLARE_SERIALIZER(UMyClass)
Declare Class 매크로는 UClass의 뼈대를 정의하고 StaticClass() 함수의 위치를 지정하는 매우 중요한 역할을 합니다. 이 매크로의 주요 파라미터는 다음과 같습니다.
/*-----------------------------------------------------------------------------
Class declaration macros.
-----------------------------------------------------------------------------*/
#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.*/
static constexpr EClassFlags StaticClassFlags=EClassFlags(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;
}
/* Eliminate V1062 warning from PVS-Studio while keeping MSVC and Clang happy. */
inline void operator delete(void* InMem)
{
::operator delete(InMem);
}
#include "UObject/GeneratedCppIncludes.h"
#include "ReflectionTest/MyClass.h"
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void EmptyLinkFunctionForGeneratedCodeMyClass() {}
// Cross Module References
COREUOBJECT_API UClass* Z_Construct_UClass_UInterface();
COREUOBJECT_API UClass* Z_Construct_UClass_UObject(); // Makes Sure UObject's UClass is registered
REFLECTIONTEST_API UClass* Z_Construct_UClass_UMyClass(); // Reference to UObject's UClass.
REFLECTIONTEST_API UClass* Z_Construct_UClass_UMyClass_NoRegister(); // Construct UClass object for UMyClass (without
registering).
REFLECTIONTEST_API UClass* Z_Construct_UClass_UMyInterface();
REFLECTIONTEST_API UClass* Z_Construct_UClass_UMyInterface_NoRegister();
REFLECTIONTEST_API UEnum* Z_Construct_UEnum_ReflectionTest_EMyEnum();
REFLECTIONTEST_API UScriptStruct* Z_Construct_UScriptStruct_FMyStruct();
UPackage* Z_Construct_UPackage__Script_ReflectionTest(); // Construct UPackage for MyClass, I think this is Unused. There
is no implementation.U
// End Cross Module References
void UMyClass::StaticRegisterNativesUMyClass()
{
UClass* Class = UMyClass::StaticClass();
static const FNameNativePtrPair Funcs[] = {
{ "CallableFunc", &UMyClass::execCallableFunc },
{ "NativeFunc", &UMyClass::execNativeFunc },
};
FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs));
}
IMPLEMENT_CLASS_NO_AUTO_REGISTRATION(UMyClass); // Important Used to Implement UCLASS
UClass* Z_Construct_UClass_UMyClass_NoRegister()
{
return UMyClass::StaticClass(); // Here gives us Direct Access to UClass Object
}
UClass* Z_Construct_UClass_UMyClass()
{
if (!Z_Registration_Info_UClass_UMyClass.OuterSingleton)
{
UECodeGen_Private::ConstructUClass(Z_Registration_Info_UClass_UMyClass.OuterSingleton,
Z_Construct_UClass_UMyClass_Statics::ClassParams);
}
return Z_Registration_Info_UClass_UMyClass.OuterSingleton;
}
void ConstructUClass(UClass*& OutClass, const FClassParams& Params)
{
if (OutClass && (OutClass->ClassFlags & CLASS_Constructed)) // Prevents Duplicate Registration
{
return;
}
/**
The For Loop below,
Can see the Array of Function pointers are called for Macros Z_Construct_UClass_UObject
Z_Construct_UPackage__Script Macro is also called to created UPackage
UObject* (*const Z_Construct_UClass_UMyClass_Statics::DependentSingletons[])() = {
(UObject* (*)())Z_Construct_UClass_UObject,
(UObject* (*)())Z_Construct_UPackage__Script_ReflectionTest,
};
*/
for (UObject* (*const *SingletonFunc)() = Params.DependencySingletonFuncArray, *(*const *SingletonFuncEnd)() =
SingletonFunc + Params.NumDependencySingletons; SingletonFunc != SingletonFuncEnd; ++SingletonFunc)
{
(*SingletonFunc)();
}
// Params.ClassNoRegister comes from UECodeGen_Private::FClassParams
// &UMyClass::StaticClass
// This is the Function Pointer that gets us UMyClass Object which is called
// by &UMyClass::StaticClass() at this point ::StaticClass() is already defined
UClass* NewClass = Params.ClassNoRegisterFunc();
OutClass = NewClass;
if (NewClass->ClassFlags & CLASS_Constructed)
{
return;
}
UObjectForceRegistration(NewClass); // Register itself to extract information.
UClass* SuperClass = NewClass->GetSuperClass();
if (SuperClass)
{
NewClass->ClassFlags |= (SuperClass->ClassFlags & CLASS_Inherit);
}
// This Adds Class_Constructed Flags and Required API Flags
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->ReferenceTokens.Reset();
}
/**
CreateLinkAndAddChildFunctionsToMap Creates the Function Pointer for Native Functions
of this class. This then adds native functions to an internal native Function Table
with a unicode name. This is used when generating code from blueprints.
This struct maps a string anme to a native function.
FPropertyParamsBase is the struct, we basically create Property pointers, See Collection Phase
*/
NewClass->CreateLinkAndAddChildFunctionsToMap(Params.FunctionLinkArray, Params.NumFunctions);
/**
Inside UObjectGlobals.cpp Line 5552 we see this function inside namespace UECodeGen_Private
Creates the Prop objects. FProperties are Inherited from FField.
*/
ConstructFProperties(NewClass, Params.PropertyArray, Params.NumProperties);
if (Params.ClassConfigNameUTF8)
{
NewClass->ClassConfigName = FName(UTF8_TO_TCHAR(Params.ClassConfigNameUTF8));
}
// Checks if it's abstract class
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_METADATA
AddMetaData(NewClass, Params.MetaDataArray, Params.NumMetaData);
#endif
// Perform "static" linking, Wrapper Function UStruct::Link(..)
NewClass->StaticLink();
// Uses some SideCar Design pattern to extend functionality of the USuperStruct?
NewClass->SetSparseClassDataStruct(NewClass->GetSparseClassDataArchetypeStruct());
}
매크로 IMPLEMENT_CLASS_NO_AUTO_REGISTRATION(UMyClass)는 클래스 정보를 GetPrivateStaticClassBody 함수로 전달하는 역할을 합니다. 이 함수는 UMyClass 타입의 스켈레톤 객체를 생성하는 책임을 집니다. 여기서 설명하는 내용은 CPP에서 DECLARE_CLASS() 매크로가 실제로 어떻게 구현되는지에 대한 부분입니다.
// Implement the GetPrivateStaticClass and the registration info but do not auto register the class.
// This is primarily used by UnrealHeaderTool
#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),
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;
}
class REFLECTIONTEST_API UMyClass : public UObject
{
private:
static void StaticRegisterNativesUMyClass();
friend struct Z_Construct_UClass_UMyClass_Statics;
private:
TClass& operator=(TClass&&);
TClass& operator=(const TClass&);
TRequiredAPI static UClass* GetPrivateStaticClass();
public:
/** Bitwise union of #EClassFlags pertaining to this class.*/
static constexpr EClassFlags StaticClassFlags=EClassFlags(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;
}
/* Eliminate V1062 warning from PVS-Studio while keeping MSVC and Clang happy. */
inline void operator delete(void* InMem)
{
::operator delete(InMem);
}
// Serializer Macro
friend FArchive &operator<<( FArchive& Ar, TClass*& Res )
{
return Ar << (UObject*&)Res;
}
friend void operator<<(FStructuredArchive::FSlot InSlot, TClass*& Res)
{
InSlot << (UObject*&)Res;
}
7/** Standard constructor, called after all reflected properties have been initialized */
NO_API UMyClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass(X); }
private: /** Private move- and copy-constructors, should never be used */ NO_API UMyClass(UMyClass&&); NO_API
UMyClass(const UMyClass&);
// Cross Module References CoreUObject
COREUOBJECT_API UClass* Z_Construct_UClass_UObject();
REFLECTIONTEST_API UClass* Z_Construct_UClass_UMyClass();
REFLECTIONTEST_API UClass* Z_Construct_UClass_UMyClass_NoRegister();
UPackage* Z_Construct_UPackage__Script_ReflectionTest();
void EmptyLinkFunctionForGeneratedCodeMyClass() {}
DEFINE_FUNCTION(UMyClass::execNativeFunc)
{
P_FINISH;
P_NATIVE_BEGIN;
P_THIS->NativeFunc_Implementation();
P_NATIVE_END;
}
DEFINE_FUNCTION(UMyClass::execCallableFunc)
{
P_FINISH;
P_NATIVE_BEGIN;
P_THIS->CallableFunc();
P_NATIVE_END;
}
static FName NAME_UMyClass_ImplementableFunc = FName(TEXT("ImplementableFunc"));
void UMyClass::ImplementableFunc()
{
ProcessEvent(FindFunctionChecked(NAME_UMyClass_ImplementableFunc),NULL);
}
static FName NAME_UMyClass_NativeFunc = FName(TEXT("NativeFunc"));
void UMyClass::NativeFunc()
{
ProcessEvent(FindFunctionChecked(NAME_UMyClass_NativeFunc),NULL);
}
void UMyClass::StaticRegisterNativesUMyClass()
{
UClass* Class = UMyClass::StaticClass();
static const FNameNativePtrPair Funcs[] = {
{ "CallableFunc", &UMyClass::execCallableFunc },
{ "NativeFunc", &UMyClass::execNativeFunc },
};
FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs));
}
// Define FuncParameters and Metadata Params
struct Z_Construct_UFunction_UMyClass_CallableFunc_Statics
{
#if WITH_METADATA
static const UECodeGen_Private::FMetaDataPairParam Function_MetaDataParams[];
#endif
static const UECodeGen_Private::FFunctionParams FuncParams;
};
// Static Defines for the Collection Phase
struct Z_Construct_UClass_UMyClass_Statics
{
static UObject* (*const DependentSingletons[])();
static const FClassFunctionLinkInfo FuncInfo[];
#if WITH_METADATA
static const UECodeGen_Private::FMetaDataPairParam Class_MetaDataParams[];
#endif
#if WITH_METADATA
static const UECodeGen_Private::FMetaDataPairParam NewProp_Health_MetaData[];
#endif
static const UECodeGen_Private::FUnsizedIntPropertyParams NewProp_Health;
static const UECodeGen_Private::FPropertyParamsBase* const PropPointers[];
static const FCppClassTypeInfoStatic StaticCppClassTypeInfo;
static const UECodeGen_Private::FClassParams ClassParams;
};
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),
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;
}
UClass* Z_Construct_UClass_UMyClass_NoRegister()
{
return UMyClass::StaticClass();
}
UObject* (*const Z_Construct_UClass_UMyClass_Statics::DependentSingletons[])() = {
(UObject* (*)())Z_Construct_UClass_UObject,
(UObject* (*)())Z_Construct_UPackage__Script_ReflectionTest,
};
const FClassFunctionLinkInfo Z_Construct_UClass_UMyClass_Statics::FuncInfo[] = {
{ &Z_Construct_UFunction_UMyClass_CallableFunc, "CallableFunc" }, // 2296517141
{ &Z_Construct_UFunction_UMyClass_ImplementableFunc, "ImplementableFunc" }, // 1412324750
{ &Z_Construct_UFunction_UMyClass_NativeFunc, "NativeFunc" }, // 1217410402
};
#if WITH_METADATA
const UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_UMyClass_Statics::Class_MetaDataParams[] = {
{ "IncludePath", "MyClass.h" },
{ "ModuleRelativePath", "MyClass.h" },
{ "ObjectInitializerConstructorDeclared", "" },
};
#endif
#if WITH_METADATA
const UECodeGen_Private::FMetaDataPairParam Z_Construct_UClass_UMyClass_Statics::NewProp_Health_MetaData[] = {
{ "ModuleRelativePath", "MyClass.h" },
};
#endif
const FClassRegisterCompiledInInfo
Z_CompiledInDeferFile_FID_Users_StaticJPL_Documents_Unreal_Projects_ReflectionTest_Source_ReflectionTest_MyClass_h_Statics::ClassInfo[]
= {
{ Z_Construct_UClass_UMyInterface, UMyInterface::StaticClass, TEXT("UMyInterface"),
&Z_Registration_Info_UClass_UMyInterface, CONSTRUCT_RELOAD_VERSION_INFO(FClassReloadVersionInfo, sizeof(UMyInterface),
4078863325U) },
{ Z_Construct_UClass_UMyClass, UMyClass::StaticClass, TEXT("UMyClass"), &Z_Registration_Info_UClass_UMyClass,
CONSTRUCT_RELOAD_VERSION_INFO(FClassReloadVersionInfo, sizeof(UMyClass), 138299640U) },
};
ConstructUClass 과정에서 리플렉션 코드 내 (*SingletonFunc)->Z_Registration_Info_UPackage__Script_ReflectionTest() 호출은 ReflectionTest 프로젝트의 UPackage 생성을 시작합니다.
#include "UObject/GeneratedCppIncludes.h"
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void EmptyLinkFunctionForGeneratedCodeReflectionTest_init() {}
static FPackageRegistrationInfo Z_Registration_Info_UPackage__Script_ReflectionTest;
FORCENOINLINE UPackage* Z_Construct_UPackage__Script_ReflectionTest()
{
if (!Z_Registration_Info_UPackage__Script_ReflectionTest.OuterSingleton)
{
static const UECodeGen_Private::FPackageParams PackageParams = {
"/Script/ReflectionTest",
nullptr,
0,
PKG_CompiledIn | 0x00000000,
0x68D52929,
0x02EA1D27,
METADATA_PARAMS(nullptr, 0)
};
UECodeGen_Private::ConstructUPackage(Z_Registration_Info_UPackage__Script_ReflectionTest.OuterSingleton,
PackageParams);
}
return Z_Registration_Info_UPackage__Script_ReflectionTest.OuterSingleton;
}
static FRegisterCompiledInInfo
Z_CompiledInDeferPackage_UPackage__Script_ReflectionTest(Z_Construct_UPackage__Script_ReflectionTest,
TEXT("/Script/ReflectionTest"), Z_Registration_Info_UPackage__Script_ReflectionTest,
CONSTRUCT_RELOAD_VERSION_INFO(FPackageReloadVersionInfo, 0x68D52929, 0x02EA1D27));
PRAGMA_ENABLE_DEPRECATION_WARNINGS
GetPrivateStaticClassBody는 UClass의 스캐폴딩 역할을 합니다. 핫 리로드(hot reload)를 위해 패키지를 가져오고, 이후 InitializePrivateStaticClass가 호출됩니다. 이때 UClass의 OuterPrivate 멤버 변수는 게임 모듈의 패키지에 의해 설정됩니다. 중요한 점은, 실제로 이 값이 생성되어 할당되는 시점은 Object->Deferred가 호출될 때라는 것입니다. 이 과정에서 GetPrivateStaticClass는 함수에 대한 참조를 전달받게 됩니다.
UENUM()
enum EMyEnum : int
{
E_Num1,
E_Num2,
E_Num3,
};
#define FOREACH_ENUM_EMYENUM(op) op(E_Num1) op(E_Num2) op(E_Num3)
enum EMyEnum : int;
template<> REFLECTIONTEST_API UEnum* StaticEnum<EMyEnum>();
static FEnumRegistrationInfo Z_Registration_Info_UEnum_EMyEnum;
static UEnum* EMyEnum_StaticEnum()
{
if (!Z_Registration_Info_UEnum_EMyEnum.OuterSingleton)
{
Z_Registration_Info_UEnum_EMyEnum.OuterSingleton = GetStaticEnum(Z_Construct_UEnum_ReflectionTest_EMyEnum,
(UObject*)Z_Construct_UPackage__Script_ReflectionTest(), TEXT("EMyEnum"));
}
return Z_Registration_Info_UEnum_EMyEnum.OuterSingleton;
}
template<> REFLECTIONTEST_API UEnum* StaticEnum<EMyEnum>()
{
return EMyEnum_StaticEnum();
}
struct Z_Construct_UEnum_ReflectionTest_EMyEnum_Statics
{
static const UECodeGen_Private::FEnumeratorParam Enumerators[];
#if WITH_METADATA
static const UECodeGen_Private::FMetaDataPairParam Enum_MetaDataParams[];
#endif
static const UECodeGen_Private::FEnumParams EnumParams;
};
const UECodeGen_Private::FEnumeratorParam Z_Construct_UEnum_ReflectionTest_EMyEnum_Statics::Enumerators[] = {
{ "E_Num1", (int64)E_Num1 },
{ "E_Num2", (int64)E_Num2 },
{ "E_Num3", (int64)E_Num3 },
};
#if WITH_METADATA
const UECodeGen_Private::FMetaDataPairParam Z_Construct_UEnum_ReflectionTest_EMyEnum_Statics::Enum_MetaDataParams[] = {
{ "E_Num1.Name", "E_Num1" },
{ "E_Num2.Name", "E_Num2" },
{ "E_Num3.Name", "E_Num3" },
{ "ModuleRelativePath", "MyClass.h" },
};
#endif
const UECodeGen_Private::FEnumParams Z_Construct_UEnum_ReflectionTest_EMyEnum_Statics::EnumParams = {
(UObject*(*)())Z_Construct_UPackage__Script_ReflectionTest,
nullptr,
"EMyEnum",
"EMyEnum",
Z_Construct_UEnum_ReflectionTest_EMyEnum_Statics::Enumerators,
UE_ARRAY_COUNT(Z_Construct_UEnum_ReflectionTest_EMyEnum_Statics::Enumerators),
RF_Public|RF_Transient|RF_MarkAsNative,
EEnumFlags::None,
(uint8)UEnum::ECppForm::Regular,
METADATA_PARAMS(Z_Construct_UEnum_ReflectionTest_EMyEnum_Statics::Enum_MetaDataParams,
UE_ARRAY_COUNT(Z_Construct_UEnum_ReflectionTest_EMyEnum_Statics::Enum_MetaDataParams))
};
UEnum* Z_Construct_UEnum_ReflectionTest_EMyEnum()
{
if (!Z_Registration_Info_UEnum_EMyEnum.InnerSingleton)
{
UECodeGen_Private::ConstructUEnum(Z_Registration_Info_UEnum_EMyEnum.InnerSingleton,
Z_Construct_UEnum_ReflectionTest_EMyEnum_Statics::EnumParams);
}
return Z_Registration_Info_UEnum_EMyEnum.InnerSingleton;
}
MyEnum의 구조는 Z_Construct_UEnum_ReflectionTest_EMyEnum() 함수 외에 추가적인 함수가 없습니다. 상단부터 살펴보면,
static FEnumRegistrationInfo Z_Registration_Info_UEnum_EMyEnum;
컬렉션 과정에서 MyEnum의 타입 정보는 FEnumRegistrationInfo에 채워집니다. 이 과정에서는 Enumerators[], FMetadatasPairParams[], 그리고 주요 데이터 구조체인 FEnumParams가 구성됩니다. 특히 FEnumParams는 패키지 함수 포인터를 받아 플래그를 설정하고, 수집된 정보를 조직하는 중심 역할을 합니다.
Enum 객체의 생성은 다음과 같이 이루어집니다.
REFLECTIONTEST_API UEnum* Z_Construct_UEnum_ReflectionTest_EMyEnum();
이 함수는 다시 UECodeGen_Private::ConstructUEnum을 호출합니다.
void ConstructUEnum(UEnum*& OutEnum, const FEnumParams& Params)
{
UObject* (*OuterFunc)() = Params.OuterFunc;
UObject* Outer = OuterFunc ? OuterFunc() : nullptr;
if (OutEnum)
{
return;
}
UEnum* NewEnum = new (EC_InternalUseOnlyConstructor, Outer, UTF8_TO_TCHAR(Params.NameUTF8), Params.ObjectFlags)
UEnum(FObjectInitializer());
OutEnum = NewEnum;
TArray<TPair<FName, int64>> EnumNames;
EnumNames.Reserve(Params.NumEnumerators);
for (const FEnumeratorParam* Enumerator = Params.EnumeratorParams, *EnumeratorEnd = Enumerator +
Params.NumEnumerators; Enumerator != EnumeratorEnd; ++Enumerator)
{
EnumNames.Emplace(UTF8_TO_TCHAR(Enumerator->NameUTF8), Enumerator->Value);
}
const bool bAddMaxKeyIfMissing = true;
NewEnum->SetEnums(EnumNames, (UEnum::ECppForm)Params.CppForm, Params.EnumFlags, bAddMaxKeyIfMissing);
NewEnum->CppType = UTF8_TO_TCHAR(Params.CppTypeUTF8);
if (Params.DisplayNameFunc)
{
NewEnum->SetEnumDisplayNameFn(Params.DisplayNameFunc);
}
#if WITH_METADATA
AddMetaData(NewEnum, Params.MetaDataArray, Params.NumMetaData);
#endif
UEnum의 주소 포인터가 전달되고, 함수 포인터들은 정적 EEnumParams 구조체에 제공됩니다. TRegistrationInfo는 FEnumRegistrationInfo를 사용한다고 표시되어 있습니다. 이 구조체는 내부 및 외부 싱글턴에 대한 포인터를 보관하는데, 여기서 '내부(inner)'는 자기 자신을, '외부(outer)'는 패키지를 의미합니다. 수집(컬렉션) 단계에서는 '외부'와 '내부'의 의미가 다르다는 점에 유의해야 합니다.
RegisterCompiledInfo 함수는 정적 객체의 파라미터를 전달받아 FRegisterCompiledInfo의 생성자에서 호출됩니다. 이때 C++ 퍼펙트 포워딩(perfect forwarding)이 사용되어, 템플릿에서 RValue와 LValue 문제를 우회합니다. 이 기법은 C++11부터 제공되며, 매우 특정한 상황에서 활용됩니다. 엔진 소스에서는 RegisterCompiledInfo 함수가 UClass, UStruct, UEnum 등 다양한 템플릿 버전으로 오버로드되어 있으며, 이를 위해 퍼펙트 포워딩이 사용된다는 점을 확인할 수 있습니다.
struct FRegisterCompiledInInfo
{
template <typename ... Args>
FRegisterCompiledInInfo(Args&& ... args)
{
RegisterCompiledInInfo(std::forward<Args>(args)...);
}
};
RegisterCompiledInInfo(class UEnum* (*InOuterRegister)(), const TCHAR* InPackageName, const TCHAR* InName,
FEnumRegistrationInfo& InInfo, const FEnumReloadVersionInfo& InVersionInfo)
class UEnum *GetStaticEnum(class UEnum *(*InRegister)(), UObject* EnumOuter, const TCHAR* EnumName)
{
UEnum *Result = (*InRegister)();
NotifyRegistrationEvent(*EnumOuter->GetOutermost()->GetName(), EnumName, ENotifyRegistrationType::NRT_Enum,
ENotifyRegistrationPhase::NRP_Finished, nullptr, false, Result);
return Result;
}
GetStaticEnum 함수는 MyEnum의 스켈레톤 객체를 생성하는 역할을 합니다. 이 함수의 참조는 EnumInfo[]에 저장되며, 이후 이 정보는 정적 구조체인 FRegisterCompiledInInfo 타입에 전달됩니다.