UnrealC++로 구조체(USTURCT)를 캐스트해 보기

소개



UnrealC++에는 "Unreal Property System"이라는 리플렉션 시스템이 있습니다.
언리얼 프로퍼티 시스템 (반사)
리플렉션이란 런타임시 프로그램의 구조(클래스의 상속 관계나 어떤 함수나 변수를 가지고 있는지 등...) 취득하거나 설정하거나 할 수 있는 구조입니다.
리플렉션 (정보 공학)
표준 C++에는 현재 리플렉션 기능이 없지만 UnrealC++에는 자체 리플렉션 시스템이 내장되어 있으므로 문자열에서 클래스와 함수를 검색하거나 Cast 함수를 사용하여 클래스를 캐스팅 할 수 있습니다. .

구조체를 Cast 함수로 캐스트 해보십시오.



TestStructs.h
USTRUCT(BlueprintType)
struct FTestStructBase
{
    GENERATED_BODY()
};

USTRUCT(BlueprintType)
struct FTestStructA : public FTestStructBase
{
    GENERATED_BODY()

public:
    UPROPERTY(BlueprintReadWrite, EditAnywhere)
    int32 TestInteger;
};

USTRUCT(BlueprintType)
struct FTestStructB : public FTestStructBase
{
    GENERATED_BODY()

public:
    UPROPERTY(BlueprintReadWrite, EditAnywhere)
    FString TestString;
};

USTRUCT(BlueprintType)
struct FTestStructC : public FTestStructBase
{
    GENERATED_BODY()

public:
    UPROPERTY(BlueprintReadWrite, EditAnywhere)
    FVector TestVector;
};

우선은 베이스가 되는 구조체와 그 파생을 3개 정의해 보겠습니다.

TestFunctionLibrary.cpp
void TestFunction(const FTestStructBase& TestStruct)
{
    if (FTestStructA* StructA = Cast<FTestStructA>(&TestStruct))
    {

    }
}

이렇게 일반 UClass처럼 Cast를 시도하면 다음 컴파일 오류가 발생합니다.
'To *TCastImpl<From,To,ECastType::UObjectToUObject>::DoCast(UObject *)': cannot convert argument 1 from 'From *' to 'UObject *'

Cast 함수를 사용할 수 있는 것은 UObject를 상속한 클래스와 같습니다. 당연히 UStruct이지만 구조체 자체는 UObject를 상속하지 않으므로이 함수를 사용할 수 없습니다.

구조체를 dynamic_cast로 캐스팅 해보기



Cast 함수를 사용할 수 없어도 C++에는 「dynamic_cast」가 있다! 그렇게 해 보겠습니다.

분명히 매크로로 대체되어 버리는 일반 dynamic_cast는 사용할 수없는 것 같습니다.
무엇으로 대체될까 하면 "UE4Casts_Private::DynamicCast"라는 함수로 바뀌는 것 같습니다.

Engine\Source\Runtime\CoreUObject\Public\Templates\Casts.h(476)
#define dynamic_cast UE4Casts_Private::DynamicCast

4 종류 정도 DynamicCast 함수가 정의되어 있습니다만, 어쩐지 사용해 주었으면 하는 것 같은 분위기...
그리고 역시 사용할 수 없었습니다.
'dynamic_cast': 'FTestStructBase' is not a polymorphic type

구조체를 static_cast로 캐스트 해 봅니다.



안쪽의 손인 'static_cast'를 사용하여 ...

TestFunctionLibrary.cpp
void TestFunction(const FTestStructBase& TestStruct)
{
    if (FTestStructA* StructA = static_cast<FTestStructA*>(&TestStruct))
    {

    }
}

static_cast는 포인터 유형을 다른 포인터 유형으로 강제 변환하므로 TestStruct 변수가 FTestStructA 유형이 아닌 경우에도 StructA는 nullptr이 되지 않습니다.

UScriptStruct::GetStructCPPName으로 얻은 이름 비교



USTRUCT 로 정의한 구조체에는 StaticStruct 함수가 포함되어 있어 이 함수로 UScriptStruct 를 취득할 수 있습니다.
그리고 GetStructCPPName 함수로부터 함수명 대로 구조체의 이름을 취득할 수 있습니다.

※추기 GetStructCPPName로 취할 수 있는 이름은 GetName으로 취할 수 있는 구조체명에 접두사의 F를 붙일 수 있으므로 GetName에서도 문제 없습니다.

이 방법에서는 이하와 같이 인수나 변수 정의로 형태가 정해져 있으면 부모의 구조체의 이름이 취득되어 버리기 때문에 사용할 수 없습니다.

TestFunctionLibrary.cpp
void TestFunction(FTestStructBase TestStruct)
{
    if (UScriptStruct* ScriptStruct = InTestStruct.StaticStruct())
    {
        if (ScriptStruct->GetStructCPPName() == FTestStructA::StaticStruct()->GetStructCPPName())
        {

        }
    }
}

그러나, 다음과 같이 프로퍼티의 주사로 지정의 구조체인지를 판정하는 경우에는 사용할 수 있습니다.

TestFunctionLibrary.cpp
void TestFunction(UObject* Owner)
{
    for (TFieldIterator<FProperty> PropertyItr(Owner); PropertyItr; ++PropertyItr)
    {
        FProperty* Property = *PropertyItr;

        if (auto StructProperty = CastField<FStructProperty>(Property))
        {
            if (auto ScriptStruct = StructProperty->Struct)
            {
                if (ScriptStruct->GetStructCPPName() == FTestStructA::StaticStruct()->GetStructCPPName())
                {

                }
            }
        }
    }
}

결론



여러가지 시도한 결과 별로 좋은 방법을 찾을 수 없었습니다 ...
상속하여 사용한다면 구조체가 아닌 UObject를 상속하여 사용하는 것이 좋을 것 같습니다.
아무래도 구조체로 한다면 독자적으로 고유의 ID와 같은 것을 갖게 해 그것을 바탕으로 판정하는 등의 궁리가 필요할 것 같습니다.

뭔가 좋은 방법을 아시면 코멘트 등으로 가르쳐 주시면 감사하겠습니다.

좋은 웹페이지 즐겨찾기