EM_Task/CoreUObject/Public/Templates/Casts.h

526 lines
16 KiB
C
Raw Permalink Normal View History

2026-02-13 16:18:33 +08:00
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "UObject/Object.h"
#include "UObject/Class.h"
#include "Templates/LosesQualifiersFromTo.h"
#include "Traits/IsVoidType.h"
class AActor;
class APawn;
class APlayerController;
class FSoftClassProperty;
class UBlueprint;
class ULevel;
class UPrimitiveComponent;
class USceneComponent;
class USkeletalMeshComponent;
class USkinnedMeshComponent;
class UStaticMeshComponent;
/// @cond DOXYGEN_WARNINGS
template <class TClass>
class TSubclassOf;
/// @endcond
UE_NORETURN COREUOBJECT_API void CastLogError(const TCHAR* FromType, const TCHAR* ToType);
/**
* Metafunction which detects whether or not a class is an IInterface. Rules:
*
* 1. A UObject is not an IInterface.
* 2. A type without a UClassType typedef member is not an IInterface.
* 3. A type whose UClassType::StaticClassFlags does not have CLASS_Interface set is not an IInterface.
*
* Otherwise, assume it's an IInterface.
*/
template <typename T, bool bIsAUObject_IMPL = TPointerIsConvertibleFromTo<T, const volatile UObject>::Value>
struct TIsIInterface
{
enum
{
Value = false
};
};
template <typename T>
struct TIsIInterface<T, false>
{
template <typename U>
static char (&Resolve(typename U::UClassType*))[(U::UClassType::StaticClassFlags & CLASS_Interface) ? 2 : 1];
template <typename U>
static char (&Resolve(...))[1];
enum
{
Value = sizeof(Resolve<T>(0)) - 1
};
};
template <typename T>
struct TIsCastable
{
// It's from-castable if it's an interface or a UObject-derived type
enum
{
Value = TIsIInterface<T>::Value || TPointerIsConvertibleFromTo < T,
const volatile UObject > ::Value
};
};
template <typename T>
struct TIsCastableToPointer: TOr<TIsVoidType<T>, TIsCastable<T>>
{
// It's to-pointer-castable if it's from-castable or void
};
template <typename T>
FORCEINLINE typename TEnableIf<TIsIInterface<T>::Value, FString>::Type GetTypeName()
{
return T::UClassType::StaticClass()->GetName();
}
template <typename T>
FORCEINLINE typename TEnableIf<!TIsIInterface<T>::Value, FString>::Type GetTypeName()
{
return T::StaticClass()->GetName();
}
enum class ECastType
{
UObjectToUObject,
InterfaceToUObject,
UObjectToInterface,
InterfaceToInterface,
FromCastFlags
};
template <typename Type>
struct TCastFlags
{
static const EClassCastFlags Value = CASTCLASS_None;
};
template <typename From, typename To, bool bFromInterface = TIsIInterface<From>::Value, bool bToInterface = TIsIInterface<To>::Value, EClassCastFlags CastClass = TCastFlags<To>::Value>
struct TGetCastType
{
#if USTRUCT_FAST_ISCHILDOF_IMPL == USTRUCT_ISCHILDOF_STRUCTARRAY
static const ECastType Value = ECastType::UObjectToUObject;
#else
static const ECastType Value = ECastType::FromCastFlags;
#endif
};
template <typename From, typename To>
struct TGetCastType<From, To, false, false, CASTCLASS_None>
{
static const ECastType Value = ECastType::UObjectToUObject;
};
template <typename From, typename To>
struct TGetCastType<From, To, false, true, CASTCLASS_None>
{
static const ECastType Value = ECastType::UObjectToInterface;
};
template <typename From, typename To, EClassCastFlags CastClass>
struct TGetCastType<From, To, true, false, CastClass>
{
static const ECastType Value = ECastType::InterfaceToUObject;
};
template <typename From, typename To, EClassCastFlags CastClass>
struct TGetCastType<From, To, true, true, CastClass>
{
static const ECastType Value = ECastType::InterfaceToInterface;
};
template <typename To, typename From>
To* Cast(From* Src);
template <typename From, typename To, ECastType CastType = TGetCastType<From, To>::Value>
struct TCastImpl
{
// This is the cast flags implementation
FORCEINLINE static To* DoCast(UObject* Src)
{
return Src && Src->GetClass()->HasAnyCastFlag(TCastFlags<To>::Value) ? (To*)Src : nullptr;
}
FORCEINLINE static To* DoCastCheckedWithoutTypeCheck(UObject* Src)
{
return (To*)Src;
}
UE_DEPRECATED(4.25, "Cast<>() and CastChecked<>() should not be used with FProperties. Use CastField<>() or CastFieldChecked<>() instead.")
FORCEINLINE static To* DoCast(FField* Src)
{
return Src && Src->IsA<To>() ? (To*)Src : nullptr;
}
UE_DEPRECATED(4.25, "Cast<>() and CastChecked<>() should not be used with FProperties. Use CastField<>() or CastFieldChecked<>() instead.")
FORCEINLINE static To* DoCastCheckedWithoutTypeCheck(FField* Src)
{
return (To*)Src;
}
};
template <typename From, typename To>
struct TCastImpl<From, To, ECastType::UObjectToUObject>
{
FORCEINLINE static To* DoCast(UObject* Src)
{
return Src && Src->IsA<To>() ? (To*)Src : nullptr;
}
FORCEINLINE static To* DoCastCheckedWithoutTypeCheck(UObject* Src)
{
return (To*)Src;
}
UE_DEPRECATED(4.25, "Cast<>() and CastChecked<>() should not be used with FProperties. Use CastField<>() or CastFieldChecked<>() instead.")
FORCEINLINE static To* DoCast(FField* Src)
{
return Src && Src->IsA<To>() ? (To*)Src : nullptr;
}
UE_DEPRECATED(4.25, "Cast<>() and CastChecked<>() should not be used with FProperties. Use CastField<>() or CastFieldChecked<>() instead.")
FORCEINLINE static To* DoCastCheckedWithoutTypeCheck(FField* Src)
{
return (To*)Src;
}
};
template <typename From, typename To>
struct TCastImpl<From, To, ECastType::InterfaceToUObject>
{
FORCEINLINE static To* DoCast(From* Src)
{
To* Result = nullptr;
if (Src)
{
UObject* Obj = Src->_getUObject();
if (Obj->IsA<To>())
{
Result = (To*)Obj;
}
}
return Result;
}
FORCEINLINE static To* DoCastCheckedWithoutTypeCheck(From* Src)
{
return Src ? (To*)Src->_getUObject() : nullptr;
}
};
template <typename From, typename To>
struct TCastImpl<From, To, ECastType::UObjectToInterface>
{
FORCEINLINE static To* DoCast(UObject* Src)
{
return Src ? (To*)Src->GetInterfaceAddress(To::UClassType::StaticClass()) : nullptr;
}
FORCEINLINE static To* DoCastCheckedWithoutTypeCheck(UObject* Src)
{
return Src ? (To*)Src->GetInterfaceAddress(To::UClassType::StaticClass()) : nullptr;
}
};
template <typename From, typename To>
struct TCastImpl<From, To, ECastType::InterfaceToInterface>
{
FORCEINLINE static To* DoCast(From* Src)
{
return Src ? (To*)Src->_getUObject()->GetInterfaceAddress(To::UClassType::StaticClass()) : nullptr;
}
FORCEINLINE static To* DoCastCheckedWithoutTypeCheck(From* Src)
{
return Src ? (To*)Src->_getUObject()->GetInterfaceAddress(To::UClassType::StaticClass()) : nullptr;
}
};
// Dynamically cast an object type-safely.
template <typename To, typename From>
FORCEINLINE To* Cast(From* Src)
{
return TCastImpl<From, To>::DoCast(Src);
}
template <class T>
FORCEINLINE T* ExactCast(UObject* Src)
{
return Src && (Src->GetClass() == T::StaticClass()) ? (T*)Src : nullptr;
}
#if DO_CHECK
// Helper function to get the full name for UObjects and UInterfaces
template <
typename T,
typename TEnableIf<!TIsDerivedFrom<T, FField>::Value>::Type* = nullptr>
FString GetFullNameForCastLogError(T* InObjectOrInterface)
{
return Cast<UObject>(InObjectOrInterface)->GetFullName();
}
// And a special version for FFields
template <
typename T,
typename TEnableIf<TIsDerivedFrom<T, FField>::Value>::Type* = nullptr>
FString GetFullNameForCastLogError(T* InField)
{
return GetFullNameSafe(InField);
}
template <typename To, typename From>
FUNCTION_NON_NULL_RETURN_START To* CastChecked(From* Src)
FUNCTION_NON_NULL_RETURN_END
{
if (!Src)
{
CastLogError(TEXT("nullptr"), *GetTypeName<To>());
}
To* Result = Cast<To>(Src);
if (!Result)
{
CastLogError(*GetFullNameForCastLogError(Src), *GetTypeName<To>());
}
return Result;
}
template <typename To, typename From>
To* CastChecked(From* Src, ECastCheckedType::Type CheckType)
{
if (Src)
{
To* Result = Cast<To>(Src);
if (!Result)
{
CastLogError(*GetFullNameForCastLogError(Src), *GetTypeName<To>());
}
return Result;
}
if (CheckType == ECastCheckedType::NullChecked)
{
CastLogError(TEXT("nullptr"), *GetTypeName<To>());
}
return nullptr;
}
#else
template <typename To, typename From>
FUNCTION_NON_NULL_RETURN_START
FORCEINLINE To*
CastChecked(From* Src)
FUNCTION_NON_NULL_RETURN_END
{
return TCastImpl<From, To>::DoCastCheckedWithoutTypeCheck(Src);
}
template <typename To, typename From>
FORCEINLINE To* CastChecked(From* Src, ECastCheckedType::Type CheckType)
{
return TCastImpl<From, To>::DoCastCheckedWithoutTypeCheck(Src);
}
#endif
// auto weak versions
template <class T, class U>
FORCEINLINE T* Cast(const TWeakObjectPtr<U>& Src)
{
return Cast<T>(Src.Get());
}
template <class T, class U>
FORCEINLINE T* ExactCast(const TWeakObjectPtr<U>& Src)
{
return ExactCast<T>(Src.Get());
}
template <class T, class U>
FORCEINLINE T* CastChecked(const TWeakObjectPtr<U>& Src, ECastCheckedType::Type CheckType = ECastCheckedType::NullChecked)
{
return CastChecked<T>(Src.Get(), CheckType);
}
// TSubclassOf versions
template <class T, class U>
FORCEINLINE T* Cast(const TSubclassOf<U>& Src)
{
return Cast<T>(*Src);
}
template <class T, class U>
FORCEINLINE T* CastChecked(const TSubclassOf<U>& Src, ECastCheckedType::Type CheckType = ECastCheckedType::NullChecked)
{
return CastChecked<T>(*Src, CheckType);
}
// Const versions of the casts
template <class T, class U>
FORCEINLINE const T* Cast(const U* Src)
{
return Cast<T>(const_cast<U*>(Src));
}
template <class T>
FORCEINLINE const T* ExactCast(const UObject* Src)
{
return ExactCast<T>(const_cast<UObject*>(Src));
}
template <class T, class U>
FORCEINLINE const T* CastChecked(const U* Src, ECastCheckedType::Type CheckType = ECastCheckedType::NullChecked)
{
return CastChecked<T>(const_cast<U*>(Src), CheckType);
}
#define DECLARE_CAST_BY_FLAG_FWD(ClassName) class ClassName;
#define DECLARE_CAST_BY_FLAG_CAST(ClassName) \
template <> \
struct TCastFlags<ClassName> \
{ \
static const EClassCastFlags Value = CASTCLASS_##ClassName; \
}; \
template <> \
struct TCastFlags<const ClassName> \
{ \
static const EClassCastFlags Value = CASTCLASS_##ClassName; \
};
#define DECLARE_CAST_BY_FLAG(ClassName) \
DECLARE_CAST_BY_FLAG_FWD(ClassName) \
DECLARE_CAST_BY_FLAG_CAST(ClassName)
#define FINISH_DECLARING_CAST_FLAGS // intentionally defined to do nothing.
// Define a macro that declares all the cast flags.
// This allows us to reuse these declarations elsewhere to define other properties for these classes.
// Note: When adding an item to this list, you must also add a CASTCLASS_ flag in ObjectBase.h and rebuild UnrealHeaderTool.
#define DECLARE_ALL_CAST_FLAGS \
DECLARE_CAST_BY_FLAG(UField) \
DECLARE_CAST_BY_FLAG(UEnum) \
DECLARE_CAST_BY_FLAG(UStruct) \
DECLARE_CAST_BY_FLAG(UScriptStruct) \
DECLARE_CAST_BY_FLAG(UClass) \
DECLARE_CAST_BY_FLAG(FProperty) \
DECLARE_CAST_BY_FLAG(FObjectPropertyBase) \
DECLARE_CAST_BY_FLAG(FObjectProperty) \
DECLARE_CAST_BY_FLAG(FWeakObjectProperty) \
DECLARE_CAST_BY_FLAG(FLazyObjectProperty) \
DECLARE_CAST_BY_FLAG(FSoftObjectProperty) \
DECLARE_CAST_BY_FLAG(FSoftClassProperty) \
DECLARE_CAST_BY_FLAG(FBoolProperty) \
DECLARE_CAST_BY_FLAG(UFunction) \
DECLARE_CAST_BY_FLAG(FStructProperty) \
DECLARE_CAST_BY_FLAG(FByteProperty) \
DECLARE_CAST_BY_FLAG(FIntProperty) \
DECLARE_CAST_BY_FLAG(FFloatProperty) \
DECLARE_CAST_BY_FLAG(FDoubleProperty) \
DECLARE_CAST_BY_FLAG(FClassProperty) \
DECLARE_CAST_BY_FLAG(FInterfaceProperty) \
DECLARE_CAST_BY_FLAG(FNameProperty) \
DECLARE_CAST_BY_FLAG(FStrProperty) \
DECLARE_CAST_BY_FLAG(FTextProperty) \
DECLARE_CAST_BY_FLAG(FArrayProperty) \
DECLARE_CAST_BY_FLAG(FDelegateProperty) \
DECLARE_CAST_BY_FLAG(FMulticastDelegateProperty) \
DECLARE_CAST_BY_FLAG(UPackage) \
DECLARE_CAST_BY_FLAG(ULevel) \
DECLARE_CAST_BY_FLAG(AActor) \
DECLARE_CAST_BY_FLAG(APlayerController) \
DECLARE_CAST_BY_FLAG(APawn) \
DECLARE_CAST_BY_FLAG(USceneComponent) \
DECLARE_CAST_BY_FLAG(UPrimitiveComponent) \
DECLARE_CAST_BY_FLAG(USkinnedMeshComponent) \
DECLARE_CAST_BY_FLAG(USkeletalMeshComponent) \
DECLARE_CAST_BY_FLAG(UBlueprint) \
DECLARE_CAST_BY_FLAG(UDelegateFunction) \
DECLARE_CAST_BY_FLAG(UStaticMeshComponent) \
DECLARE_CAST_BY_FLAG(FEnumProperty) \
DECLARE_CAST_BY_FLAG(FNumericProperty) \
DECLARE_CAST_BY_FLAG(FInt8Property) \
DECLARE_CAST_BY_FLAG(FInt16Property) \
DECLARE_CAST_BY_FLAG(FInt64Property) \
DECLARE_CAST_BY_FLAG(FUInt16Property) \
DECLARE_CAST_BY_FLAG(FUInt32Property) \
DECLARE_CAST_BY_FLAG(FUInt64Property) \
DECLARE_CAST_BY_FLAG(FMapProperty) \
DECLARE_CAST_BY_FLAG(FSetProperty) \
DECLARE_CAST_BY_FLAG(USparseDelegateFunction) \
DECLARE_CAST_BY_FLAG(FMulticastInlineDelegateProperty) \
DECLARE_CAST_BY_FLAG(FMulticastSparseDelegateProperty) \
FINISH_DECLARING_CAST_FLAGS // This is here to hopefully remind people to include the "\" in all declarations above, especially when copy/pasting the final line.
// Now actually declare the flags
DECLARE_ALL_CAST_FLAGS
#undef DECLARE_CAST_BY_FLAG
#undef DECLARE_CAST_BY_FLAG_CAST
#undef DECLARE_CAST_BY_FLAG_FWD
#if HACK_HEADER_GENERATOR
// Singleton class to get the cast flag for a given class name.
struct COREUOBJECT_API ClassCastFlagMap
{
static ClassCastFlagMap& Get();
// Get Mapped name -> cast flag. Returns CASTCLASS_None if name is not found.
EClassCastFlags GetCastFlag(const FString& ClassName) const;
private:
ClassCastFlagMap();
TMap<FString, EClassCastFlags> CastFlagMap;
};
#endif // HACK_HEADER_GENERATOR
namespace UE4Casts_Private
{
template <typename To, typename From>
FORCEINLINE typename TEnableIf<TAnd<TIsPointer<To>, TAnd<TIsCastableToPointer<typename TRemovePointer<To>::Type>, TIsCastable<From>>>::Value, To>::Type DynamicCast(From* Arg)
{
typedef typename TRemovePointer<To>::Type ToValueType;
typedef typename TRemovePointer<From>::Type FromValueType;
// Casting away const/volatile
static_assert(!TLosesQualifiersFromTo<FromValueType, ToValueType>::Value, "Conversion loses qualifiers");
// If we're casting to void, cast to UObject instead and let it implicitly cast to void
return Cast<typename TChooseClass<TIsVoidType<ToValueType>::Value, UObject, ToValueType>::Result>(Arg);
}
template <typename To, typename From>
FORCEINLINE typename TEnableIf<!TAnd<TIsPointer<To>, TAnd<TIsCastableToPointer<typename TRemovePointer<To>::Type>, TIsCastable<From>>>::Value, To>::Type DynamicCast(From* Arg)
{
return dynamic_cast<To>(Arg);
}
template <typename To, typename From>
FORCEINLINE typename TEnableIf<TAnd<TIsCastable<typename TRemoveReference<To>::Type>, TIsCastable<typename TRemoveReference<From>::Type>>::Value, To>::Type DynamicCast(From&& Arg)
{
typedef typename TRemoveReference<From>::Type FromValueType;
typedef typename TRemoveReference<To>::Type ToValueType;
// Casting away const/volatile
static_assert(!TLosesQualifiersFromTo<FromValueType, ToValueType>::Value, "Conversion loses qualifiers");
// T&& can only be cast to U&&
// http://en.cppreference.com/w/cpp/language/dynamic_cast
static_assert(TOr<TIsLValueReferenceType<From>, TIsRValueReferenceType<To>>::Value, "Cannot dynamic_cast from an rvalue to a non-rvalue reference");
return Forward<To>(*CastChecked<typename TRemoveReference<To>::Type>(&Arg));
}
template <typename To, typename From>
FORCEINLINE typename TEnableIf<!TAnd<TIsCastable<typename TRemoveReference<To>::Type>, TIsCastable<typename TRemoveReference<From>::Type>>::Value, To>::Type DynamicCast(From&& Arg)
{
// This may fail when dynamic_casting rvalue references due to patchy compiler support
return dynamic_cast<To>(Arg);
}
} // namespace UE4Casts_Private
#define dynamic_cast UE4Casts_Private::DynamicCast