본문 바로가기

공부/Unreal5

Unreal Engine Cast

C++ 캐스팅은 https://wkals1213.tistory.com/77

 

C++ Cast

암시적 캐스팅과 명시적 캐스팅이 있다. 암시적 캐스팅은 컴파일러가 자동으로 컴파일을 해줄때입니다. 명시적캐스팅은 프로그래머가 코드를 직접 작성하여 캐스팅을 해줄때입니다. 캐스팅은

wkals1213.tistory.com

.

 

Unreal에도 형변환하기위한 Cast 함수는 업캐스트 및 다운캐스트를 수행하는 방법을 제공하는

리플렉션 시스템이다

template <typename To, typename From>
FORCEINLINE To* Cast(From* Src)
{
	return TCastImpl<From, To>::DoCast(Src);
}

TCastImpl 즉 내부에 작동되는 함수는 타입에 맞게 템플릿으로 구성이 되있는데.

(타입은 UObjectToUObject, InterfaceToUObject, UObjectToInterface, InterfaceToInterface)

DoCast

변환할수있는지 IsA함수를 통해 확인을 거친 후 static_cast를 수행하게됩니다.

 

template <typename From, typename To>
struct TCastImpl<From, To, ECastType::InterfaceToUObject>
{
	FORCEINLINE static To* DoCast( From* Src )
	{
		if (Src)
		{
			UObject* Obj = Src->_getUObject();
			if (Obj && Obj->IsA<To>())
			{
				return (To*)Obj;
			}
		}
		return nullptr;
	}

	FORCEINLINE static To* DoCastCheckedWithoutTypeCheck( From* Src )
	{
		return Src ? (To*)Src->_getUObject() : nullptr;
	}
};

 

IsA<>()는 다음과 같이 호출이 된다.

template <typename OtherClassType>
	FORCEINLINE bool IsA( OtherClassType SomeBase ) const
	{
		// We have a cyclic dependency between UObjectBaseUtility and UClass,
		// so we use a template to allow inlining of something we haven't yet seen, because it delays compilation until the function is called.

		// 'static_assert' that this thing is actually a UClass pointer or convertible to it.
		const UClass* SomeBaseClass = SomeBase;
		(void)SomeBaseClass;
		checkfSlow(SomeBaseClass, TEXT("IsA(NULL) cannot yield meaningful results"));

		const UClass* ThisClass = GetClass();

		// Stop the compiler doing some unnecessary branching for nullptr checks
		UE_ASSUME(SomeBaseClass);
		UE_ASSUME(ThisClass);

		return IsChildOfWorkaround(ThisClass, SomeBaseClass);
	}

	/** Returns true if this object is of the template type. */
	template<class T>
	bool IsA() const
	{
		return IsA(T::StaticClass());
	}

 

IsChildOfWorkaround를 타고 들어가면 IsChildOf라는 함수가 나온다.

IsChildOf는 대상 유형과 같은지 여부를 확인하는 함수인듯하다.

bool UStruct::IsChildOf( const UStruct* SomeBase ) const
{
	if (SomeBase == nullptr)
	{
		return false;
	}

	bool bOldResult = false;
	for ( const UStruct* TempStruct=this; TempStruct; TempStruct=TempStruct->GetSuperStruct() )
	{
		if ( TempStruct == SomeBase )
		{
			bOldResult = true;
			break;
		}
	}

#if USTRUCT_FAST_ISCHILDOF_IMPL == USTRUCT_ISCHILDOF_STRUCTARRAY
	const bool bNewResult = IsChildOfUsingStructArray(*SomeBase);
#endif

#if USTRUCT_FAST_ISCHILDOF_COMPARE_WITH_OUTERWALK
	ensureMsgf(bOldResult == bNewResult, TEXT("New cast code failed"));
#endif

	return bOldResult;
}
	inline bool IsChildOf(const FFieldClass* InClass) const
	{
		return !!(CastFlags & InClass->GetId());
	}

 

하지만 이건 Object만 비교했기 때문에 더 깊게는 안들어가겠습니다.

 

정리하자면 다음과 같습니다. 

Cast<T>는 C++의 dynamic_cast<T>를 사용하지않지만 비슷하게 동작합니다.

 

왜냐하면 UE4는 RTTI를 무효화하고, 자체 리플렉션을 구현해서 Cast<T>를 제공하고있습니다.

 

DoCast함수를 를 사용하여 변환을 수행하게 되는데

UObject는 IsA함수를 사용하여 변환할수있는지 확인 후 static_cast를 수행

Interface 변환은 GetInterfaceAddress로부터 확인해서 캐스팅을 수행하게 됩니다.

만약 안전하지 않다고 판단될 경우 nullptr를 반환하게됩니다.

 

https://usagi.hatenablog.jp/entry/2017/12/01/ac_ue4_2