#pragma once #include "CoreTypes.h" #include "Memory/Memory.h" #include "Templates/Utility.h" #include "TypeTraits/TypeTraits.h" #include "Miscellaneous/TypeInfo.h" #include "Miscellaneous/AssertionMacros.h" NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) NAMESPACE_PRIVATE_BEGIN template void* AnyCopyNew(const void* Source) { if constexpr (!TIsCopyConstructible::Value) check_no_entry(); else return new T(*reinterpret_cast(Source)); return nullptr; } using FAnyCopyNewFunc = void* (*)(const void*); template void* AnyMoveNew(void* Source) { if constexpr (!TIsMoveConstructible::Value) check_no_entry(); else return new T(MoveTemp(*reinterpret_cast(Source))); return nullptr; } using FAnyMoveNewFunc = void* (*)(void*); template void AnyDelete(void* InValue) { delete reinterpret_cast(InValue); } using FAnyDeleteFunc = void(*)(void*); template void AnyDestroy(void* InValue) { if constexpr (!TIsTriviallyDestructible::Value) { typedef T DestructOptionalType; reinterpret_cast(InValue)->DestructOptionalType::~DestructOptionalType(); } } using FAnyDestroyFunc = void(*)(void*); template void AnyCopyConstruct(void* Target, const void* Source) { if constexpr (!TIsCopyConstructible::Value) check_no_entry(); else new(reinterpret_cast(Target)) T(*reinterpret_cast(Source)); } using FAnyCopyConstructFunc = void(*)(void*, const void*); template void AnyMoveConstruct(void* Target, void* Source) { if constexpr (!TIsMoveConstructible::Value) check_no_entry(); else new(reinterpret_cast(Target)) T(MoveTemp(*reinterpret_cast(Source))); } using FAnyMoveConstructFunc = void(*)(void*, void*); template void AnyCopyAssign(void* Target, const void* Source) { if constexpr (!TIsCopyAssignable::Value) check_no_entry(); else *reinterpret_cast(Target) = *reinterpret_cast(Source); } using FAnyCopyAssignFunc = void(*)(void*, const void*); template void AnyMoveAssign(void* Target, void* Source) { if constexpr (!TIsMoveAssignable::Value) check_no_entry(); else *reinterpret_cast(Target) = MoveTemp(*reinterpret_cast(Source)); } using FAnyMoveAssignFunc = void(*)(void*, void*); struct FAnyRTTI { bool bIsInline; FAnyCopyNewFunc CopyNew; FAnyMoveNewFunc MoveNew; FAnyDeleteFunc Delete; FAnyDestroyFunc Destroy; FAnyCopyConstructFunc CopyConstruct; FAnyMoveConstructFunc MoveConstruct; FAnyCopyAssignFunc CopyAssign; FAnyMoveAssignFunc MoveAssign; }; template struct TAnyRTTIHelper { static constexpr FAnyRTTI RTTI = { bInIsInline, AnyCopyNew, AnyMoveNew, AnyDelete, AnyDestroy, AnyCopyConstruct, AnyMoveConstruct, AnyCopyAssign, AnyMoveAssign, }; }; inline constexpr size_t ANY_DEFAULT_INLINE_SIZE = 64 - sizeof(FTypeInfo) - sizeof(const FAnyRTTI*); inline constexpr size_t ANY_DEFAULT_INLINE_ALIGNMENT = Memory::MINIMUM_ALIGNMENT; NAMESPACE_PRIVATE_END template struct TAny { template struct TIsInlineStorable : TBoolConstant { }; template struct TIsTriviallyStorable : TBoolConstant::Value && TIsTrivial::Value && TIsTriviallyCopyable::Value> { }; constexpr TAny() : TypeInfo(Typeid(void)), RTTI(nullptr) { } TAny(FInvalid) : TAny() { } TAny(const TAny& InValue) : TypeInfo(InValue.TypeInfo), RTTI(InValue.RTTI) { if (!IsValid()) return; if (IsTrivial()) Memory::Memcpy(InlineValue, InValue.InlineValue); else if (IsInline()) RTTI->CopyConstruct(GetData(), InValue.GetData()); else DynamicValue = RTTI->CopyNew(InValue.GetData()); } TAny(TAny&& InValue) : TypeInfo(InValue.TypeInfo), RTTI(InValue.RTTI) { if (!IsValid()) return; if (IsTrivial()) Memory::Memcpy(InlineValue, InValue.InlineValue); else if (IsInline()) RTTI->MoveConstruct(GetData(), InValue.GetData()); else { DynamicValue = InValue.DynamicValue; InValue.TypeInfo = Typeid(void); } } template requires TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value && TIsConstructible::Type, Types...>::Value explicit TAny(TInPlaceType, Types&&... Args) : TypeInfo(Typeid(typename TDecay::Type)) { using SelectedType = typename TDecay::Type; if constexpr (TIsTriviallyStorable::Value) { new(&InlineValue) SelectedType(Forward(Args)...); RTTI = nullptr; } else if constexpr (TIsInlineStorable::Value) { new(&InlineValue) SelectedType(Forward(Args)...); RTTI = &NAMESPACE_PRIVATE::TAnyRTTIHelper::RTTI; } else { DynamicValue = new SelectedType(Forward(Args)...); RTTI = &NAMESPACE_PRIVATE::TAnyRTTIHelper::RTTI; } } template requires (!TIsSame::Type, TAny>::Value) && (!TIsInPlaceTypeSpecialization::Type>::Value) && TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value && TIsConstructible::Type, T&&>::Value TAny(T&& InValue) : TAny(InPlaceType::Type>, Forward(InValue)) { } ~TAny() { Reset(); } TAny& operator=(const TAny& InValue) { if (!InValue.IsValid()) { Reset(); } else if (TypeInfo == InValue.TypeInfo) { if (IsTrivial()) Memory::Memcpy(InlineValue, InValue.InlineValue); else RTTI->CopyAssign(GetData(), InValue.GetData()); } else { Reset(); TypeInfo = InValue.TypeInfo; RTTI = InValue.RTTI; if (IsTrivial()) Memory::Memcpy(InlineValue, InValue.InlineValue); else if (IsInline()) RTTI->CopyConstruct(GetData(), InValue.GetData()); else DynamicValue = RTTI->CopyNew(InValue.GetData()); } return *this; } TAny& operator=(TAny&& InValue) { if (!InValue.IsValid()) { Reset(); } else if (TypeInfo == InValue.TypeInfo) { if (IsTrivial()) Memory::Memcpy(InlineValue, InValue.InlineValue); else if (IsInline()) RTTI->MoveAssign(GetData(), InValue.GetData()); else { RTTI->Delete(DynamicValue); DynamicValue = InValue.DynamicValue; InValue.TypeInfo = Typeid(void); } } else { Reset(); TypeInfo = InValue.TypeInfo; RTTI = InValue.RTTI; if (IsTrivial()) Memory::Memcpy(InlineValue, InValue.InlineValue); else if (IsInline()) RTTI->MoveConstruct(GetData(), InValue.GetData()); else DynamicValue = RTTI->MoveNew(InValue.GetData()); } return *this; } template requires (!TIsSame::Type, TAny>::Value) && (!TIsInPlaceTypeSpecialization::Type>::Value) && TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value && TIsConstructible::Type, T&&>::Value TAny& operator=(T&& InValue) { using SelectedType = typename TDecay::Type; if (TypeInfo == Typeid(SelectedType)) { if constexpr (TIsTriviallyStorable::Value) Memory::Memcpy(&InlineValue, &InValue, sizeof(SelectedType)); else GetValue() = Forward(InValue); } else { Reset(); TypeInfo = Typeid(SelectedType); if constexpr (TIsTriviallyStorable::Value) { new(&InlineValue) SelectedType(Forward(InValue)); RTTI = nullptr; } else if constexpr (TIsInlineStorable::Value) { new(&InlineValue) SelectedType(Forward(InValue)); RTTI = &NAMESPACE_PRIVATE::TAnyRTTIHelper::RTTI; } else { DynamicValue = new SelectedType(Forward(InValue)); RTTI = &NAMESPACE_PRIVATE::TAnyRTTIHelper::RTTI; } } return *this; } template requires TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value && TIsConstructible::Type, T&&>::Value typename TDecay::Type& Emplace(Types&&... Args) { Reset(); using SelectedType = typename TDecay::Type; TypeInfo = Typeid(SelectedType); if constexpr (TIsTriviallyStorable::Value) { new(&InlineValue) SelectedType(Forward(Args)...); RTTI = nullptr; } else if constexpr (TIsInlineStorable::Value) { new(&InlineValue) SelectedType(Forward(Args)...); RTTI = &NAMESPACE_PRIVATE::TAnyRTTIHelper::RTTI; } else { DynamicValue = new SelectedType(Forward(Args)...); RTTI = &NAMESPACE_PRIVATE::TAnyRTTIHelper::RTTI; } return GetValue(); } constexpr FTypeInfo GetTypeInfo() const { return TypeInfo; } constexpr bool IsValid() const { return TypeInfo != Typeid(void); } constexpr explicit operator bool() const { return TypeInfo != Typeid(void); } constexpr bool IsInline() const { return RTTI != nullptr ? RTTI->bIsInline : true; } constexpr bool IsTrivial() const { return RTTI == nullptr; } template constexpr bool HoldsAlternative() const { return IsValid() ? TypeInfo == Typeid(T) : false; } constexpr void* GetData() { return IsInline() ? &InlineValue : DynamicValue; } constexpr const void* GetData() const { return IsInline() ? &InlineValue : DynamicValue; } template requires TIsSame::Type>::Value && TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value constexpr T& GetValue() & { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TAny. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast< T*>(GetData()); } template requires TIsSame::Type>::Value && TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value constexpr T&& GetValue() && { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TAny. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast< T*>(GetData())); } template requires TIsSame::Type>::Value && TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value constexpr const T& GetValue() const& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TAny. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast(GetData()); } template requires TIsSame::Type>::Value && TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value constexpr const T&& GetValue() const&& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TAny. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast(GetData())); } template requires TIsSame::Type>::Value&& TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value constexpr T& Get( T& DefaultValue) & { return HoldsAlternative() ? GetValue() : DefaultValue; } template requires TIsSame::Type>::Value&& TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value constexpr const T& Get(const T& DefaultValue) const& { return HoldsAlternative() ? GetValue() : DefaultValue; } void Reset() { if (!IsValid()) return; TypeInfo = Typeid(void); if (IsTrivial()); else if (IsInline()) RTTI->Destroy(&InlineValue); else RTTI->Delete(DynamicValue); RTTI = nullptr; } private: union { TAlignedStorage::Type InlineValue; void* DynamicValue; }; FTypeInfo TypeInfo; const NAMESPACE_PRIVATE::FAnyRTTI* RTTI; }; template constexpr bool operator==(const TAny& LHS, const T& RHS) { return LHS.template HoldsAlternative() ? LHS.template GetValue() == RHS : false; } template constexpr bool operator!=(const TAny& LHS, const T& RHS) { return LHS.template HoldsAlternative() ? LHS.template GetValue() != RHS : true; } template constexpr bool operator==(const T& LHS, const TAny& RHS) { return RHS.template HoldsAlternative() ? LHS == RHS.template GetValue() : false; } template constexpr bool operator!=(const T& LHS, const TAny& RHS) { return RHS.template HoldsAlternative() ? LHS != RHS.template GetValue() : true; } template constexpr bool operator==(const TAny& LHS, FInvalid) { return !LHS.IsValid(); } template constexpr bool operator!=(const TAny& LHS, FInvalid) { return LHS.IsValid(); } template constexpr bool operator==(FInvalid, const TAny& RHS) { return !RHS.IsValid(); } template constexpr bool operator!=(FInvalid, const TAny& RHS) { return RHS.IsValid(); } template constexpr void Swap(TAny& A, TAny& B) { if (!A && !B) return; if (A && !B) { B = MoveTemp(A); A.Reset(); return; } if (B && !A) { A = MoveTemp(B); B.Reset(); return; } TAny Temp = MoveTemp(A); A = MoveTemp(B); B = MoveTemp(Temp); } using FAny = TAny; static_assert(sizeof(FAny) == 64, "The byte size of FAny is unexpected"); NAMESPACE_MODULE_END(Utility) NAMESPACE_MODULE_END(Redcraft) NAMESPACE_REDCRAFT_END