#pragma once #include "CoreTypes.h" #include "Memory/Memory.h" #include "Templates/Utility.h" #include "Templates/TypeHash.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) *reinterpret_cast(Target) = *reinterpret_cast(Source); else if constexpr (TIsCopyConstructible::Value) { AnyDestroy(Target); AnyCopyConstruct(Target, Source); } else check_no_entry(); } using FAnyCopyAssignFunc = void(*)(void*, const void*); template void AnyMoveAssign(void* Target, void* Source) { if constexpr (TIsMoveAssignable::Value) *reinterpret_cast(Target) = MoveTemp(*reinterpret_cast(Source)); else if constexpr (TIsMoveConstructible::Value) { AnyDestroy(Target); AnyMoveConstruct(Target, Source); } else check_no_entry(); } using FAnyMoveAssignFunc = void(*)(void*, void*); //template //constexpr bool AnyEqualityOperator(const void* LHS, const void* RHS) //{ // if constexpr (!CEqualityComparable) check_no_entry(); // else return *reinterpret_cast(LHS) == *reinterpret_cast(RHS); // return false; //} // //using FAnyEqualityOperatorFunc = bool(*)(const void*, const void*); // //template //void AnySwap(void* A, void* B) //{ // if constexpr (TIsSwappable::Value) Swap(*reinterpret_cast(A), *reinterpret_cast(B)); // else check_no_entry(); //} // //using FAnySwapFunc = void(*)(void*, void*); // //template //size_t AnyTypeHash(const void* InValue) //{ // if constexpr (CHashable) return GetTypeHash(*reinterpret_cast(InValue)); // else return 3516520171; //} // //using FAnyTypeHashFunc = size_t(*)(const void*); struct FAnyRTTI { bool bIsInline; FAnyCopyNewFunc CopyNew; FAnyMoveNewFunc MoveNew; FAnyDeleteFunc Delete; FAnyDestroyFunc Destroy; FAnyCopyConstructFunc CopyConstruct; FAnyMoveConstructFunc MoveConstruct; FAnyCopyAssignFunc CopyAssign; FAnyMoveAssignFunc MoveAssign; // FAnyEqualityOperatorFunc EqualityOperator; // FAnySwapFunc SwapObject; // FAnyTypeHashFunc TypeHash; }; template struct TAnyRTTIHelper { static constexpr FAnyRTTI RTTI = { bInIsInline, AnyCopyNew, AnyMoveNew, AnyDelete, AnyDestroy, AnyCopyConstruct, AnyMoveConstruct, AnyCopyAssign, AnyMoveAssign, // AnyEqualityOperator, // AnySwap, // AnyTypeHash, }; }; NAMESPACE_PRIVATE_END inline constexpr size_t ANY_DEFAULT_INLINE_SIZE = 48; inline constexpr size_t ANY_DEFAULT_INLINE_ALIGNMENT = 16; template struct TAny { template struct TIsInlineStorable : TBoolConstant { }; template struct TIsTriviallyStorable : TBoolConstant::Value && TIsTrivial::Value && TIsTriviallyCopyable::Value> { }; constexpr TAny() : TypeInfo(nullptr), RTTI(nullptr) { } constexpr TAny(FInvalid) : TAny() { } FORCEINLINE 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()); } FORCEINLINE 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 = nullptr; } } template requires TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value && TIsConstructible::Type, Types...>::Value FORCEINLINE explicit TAny(TInPlaceType, Types&&... Args) { using SelectedType = typename TDecay::Type; EmplaceImpl(Forward(Args)...); } template requires (!TIsSame::Type, TAny>::Value) && (!TIsTInPlaceType::Type>::Value) && TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value && TIsConstructible::Type, T&&>::Value FORCEINLINE TAny(T&& InValue) : TAny(InPlaceType::Type>, Forward(InValue)) { } FORCEINLINE ~TAny() { ResetImpl(); } FORCEINLINE TAny& operator=(const TAny& InValue) { if (&InValue == this) return *this; if (!InValue.IsValid()) { Reset(); } else if (GetTypeInfo() == InValue.GetTypeInfo()) { if (IsTrivial()) Memory::Memcpy(InlineValue, InValue.InlineValue); else RTTI->CopyAssign(GetData(), InValue.GetData()); } else { ResetImpl(); 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; } FORCEINLINE TAny& operator=(TAny&& InValue) { if (&InValue == this) return *this; if (!InValue.IsValid()) { Reset(); } else if (GetTypeInfo() == InValue.GetTypeInfo()) { if (IsTrivial()) Memory::Memcpy(InlineValue, InValue.InlineValue); else if (IsInline()) RTTI->MoveAssign(GetData(), InValue.GetData()); else { RTTI->Delete(DynamicValue); DynamicValue = InValue.DynamicValue; InValue.TypeInfo = nullptr; } } else { ResetImpl(); 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) && (!TIsTInPlaceType::Type>::Value) && TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value && TIsConstructible::Type, T&&>::Value FORCEINLINE TAny& operator=(T&& InValue) { using SelectedType = typename TDecay::Type; if (HoldsAlternative()) { if constexpr (TIsTriviallyStorable::Value) Memory::Memcpy(&InlineValue, &InValue, sizeof(SelectedType)); else GetValue() = Forward(InValue); } else { Reset(); EmplaceImpl(Forward(InValue)); } return *this; } template requires TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value && TIsConstructible::Type, T&&>::Value FORCEINLINE typename TDecay::Type& Emplace(Types&&... Args) { ResetImpl(); using SelectedType = typename TDecay::Type; EmplaceImpl(Forward(Args)...); return GetValue(); } constexpr const FTypeInfo& GetTypeInfo() const { return TypeInfo != nullptr ? *TypeInfo : Typeid(void); } constexpr bool IsValid() const { return TypeInfo != nullptr; } constexpr explicit operator bool() const { return TypeInfo != nullptr; } constexpr bool IsInline() const { return RTTI != nullptr ? RTTI->bIsInline : true; } constexpr bool IsTrivial() const { return RTTI == nullptr; } template constexpr bool HoldsAlternative() const { return IsValid() ? GetTypeInfo() == 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; } FORCEINLINE void Reset() { TypeInfo = nullptr; ResetImpl(); } // constexpr size_t GetTypeHash() const // { // if (!IsValid()) return 20090007; // return HashCombine(NAMESPACE_REDCRAFT::GetTypeHash(GetTypeInfo()), RTTI->TypeHash(GetData())); // } // // constexpr void Swap(TAny& InValue) // { // if (!IsValid() && !InValue.IsValid()) return; // // if (IsValid() && !InValue.IsValid()) // { // InValue = MoveTemp(*this); // Reset(); // return; // } // // if (InValue.IsValid() && !IsValid()) // { // *this = MoveTemp(InValue); // InValue.Reset(); // return; // } // // if (GetTypeInfo() == InValue.GetTypeInfo()) // { // RTTI->SwapObject(GetData(), InValue.GetData()); // return; // } // // TAny Temp = MoveTemp(*this); // *this = MoveTemp(InValue); // InValue = MoveTemp(Temp); // } private: union { TAlignedStorage::Type InlineValue; void* DynamicValue; }; const FTypeInfo* TypeInfo; const NAMESPACE_PRIVATE::FAnyRTTI* RTTI; template FORCEINLINE void EmplaceImpl(Types&&... Args) { 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; } } FORCEINLINE void ResetImpl() { if (!IsValid() || IsTrivial()) return; else if (IsInline()) RTTI->Destroy(&InlineValue); else RTTI->Delete(DynamicValue); } // friend FORCEINLINE bool operator==(const TAny& LHS, const TAny& RHS) // { // if (LHS.GetTypeInfo() != RHS.GetTypeInfo()) return false; // if (LHS.IsValid() == false) return true; // return LHS.RTTI->EqualityOperator(LHS.GetData(), RHS.GetData()); // } }; 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, FInvalid) { return !LHS.IsValid(); } template struct TIsTAny : FFalse { }; template struct TIsTAny> : FTrue { }; 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