From a8e1852b34dc1a396d68603f7a07e8f1443e6e0a Mon Sep 17 00:00:00 2001 From: _Redstone_c_ Date: Thu, 15 Dec 2022 23:38:51 +0800 Subject: [PATCH] refactor(templates): refactor FAny to a non-template class --- .../Private/Testing/TemplatesTesting.cpp | 26 +- .../Source/Public/Templates/Any.h | 567 +++++++++--------- .../Source/Public/Templates/Function.h | 18 +- 3 files changed, 311 insertions(+), 300 deletions(-) diff --git a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp index e5eab1a..1da865e 100644 --- a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp +++ b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp @@ -448,7 +448,7 @@ void TestAny() FTracker& operator=(const FTracker& InValue) { always_check_no_entry(); return *this; } FTracker& operator=(FTracker&& InValue) { return *this; } }; - + { FAny TempA; FAny TempB(Invalid); @@ -492,7 +492,7 @@ void TestAny() always_check(!TempD.IsValid()); always_check(0 == TempA); } - + { FAny TempA; FAny TempB(Invalid); @@ -536,7 +536,7 @@ void TestAny() always_check(!TempD.IsValid()); always_check(FIntegral(0) == TempA); } - + { FAny TempA; FAny TempB(Invalid); @@ -580,7 +580,7 @@ void TestAny() always_check(!TempD.IsValid()); always_check(FFloating(0.0) == TempA); } - + { FAny TempA; FAny TempB(InPlaceType, 0); @@ -624,24 +624,6 @@ void TestAny() TempZ = FTracker(); } - { - always_check(GetTypeHash(FAny(114)) == GetTypeHash(FAny(114))); - always_check(GetTypeHash(FAny(114)) != GetTypeHash(FAny(514))); - } - - { - FAny TempA = Invalid; - FAny TempB = static_cast(16); - FAny TempC = static_cast(16); - FAny TempD = static_cast(32); - - always_check(TempA == TempA); - always_check(TempA != TempB); - always_check(TempB != TempC); - always_check(TempB != TempC); - always_check(TempD >= TempC); - always_check(TempA <=> TempB == partial_ordering::unordered); - } } void TestTuple() diff --git a/Redcraft.Utility/Source/Public/Templates/Any.h b/Redcraft.Utility/Source/Public/Templates/Any.h index 8cb6bd9..f5a68d0 100644 --- a/Redcraft.Utility/Source/Public/Templates/Any.h +++ b/Redcraft.Utility/Source/Public/Templates/Any.h @@ -13,122 +13,67 @@ NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) -// TAny's CustomStorage concept, see FAnyDefaultStorage -template -concept CAnyCustomStorage = CDefaultConstructible - && !CCopyConstructible && !CMoveConstructible - && !CCopyAssignable && !CMoveAssignable - && CDestructible - && CSameAs - && CSameAs - && requires(const T& A) - { - { A.InlineAllocation() } -> CSameAs; - { A.HeapAllocation() } -> CSameAs; - { A.TypeInfo() } -> CSameAs; - } - && requires(T& A) - { - { A.InlineAllocation() } -> CSameAs; - { A.HeapAllocation() } -> CSameAs; - { A.TypeInfo() } -> CSameAs; - } - && requires(T& A, const T& B, T&& C) - { - A.CopyCustom(B); - A.MoveCustom(MoveTemp(C)); - }; +// NOTE: In the STL, the assignment operation of the std::any type uses the copy-and-swap idiom +// instead of directly calling the assignment operation of the contained value. +// The purpose of this is as follows: +// 1) the copy assignment might not exist. +// 2) the typical case is that the objects are different. +// 3) it is less exception-safe +// But we don't follow the the copy-and-swap idiom, because we assume that no function throws an exception. -// TAny's default storage structure -struct alignas(16) FAnyDefaultStorage : FSingleton -{ - // The built-in copy/move operators are disabled and CopyCustom/MoveCustom is used instead of them - - // You can add custom variables like this - //Type Variable; - - //~ Begin CAnyCustomStorage Interface - inline static constexpr size_t InlineSize = 64 - sizeof(uintptr); - inline static constexpr size_t InlineAlignment = 16; - constexpr void* InlineAllocation() { return &InlineAllocationImpl; } - constexpr const void* InlineAllocation() const { return &InlineAllocationImpl; } - constexpr void*& HeapAllocation() { return HeapAllocationImpl; } - constexpr void* HeapAllocation() const { return HeapAllocationImpl; } - constexpr uintptr& TypeInfo() { return TypeInfoImpl; } - constexpr uintptr TypeInfo() const { return TypeInfoImpl; } - constexpr void CopyCustom(const FAnyDefaultStorage& InValue) { /* Variable = InValue.Variable; */ } // You just need to copy the custom variables - constexpr void MoveCustom( FAnyDefaultStorage&& InValue) { /* Variable = MoveTemp(InValue.Variable); */ } // You just need to move the custom variables - //~ End CAnyCustomStorage Interface - - union - { - uint8 InlineAllocationImpl[InlineSize]; - void* HeapAllocationImpl; - }; - - uintptr TypeInfoImpl; - -}; - -static_assert(CAnyCustomStorage); - -// You can add custom storage area through CustomStorage, such as TFunction -// It is not recommended to use this, FAny is recommended -template -class TAny +class alignas(16) FAny { public: - inline static constexpr size_t InlineSize = CustomStorage::InlineSize; - inline static constexpr size_t InlineAlignment = CustomStorage::InlineAlignment; + FORCEINLINE constexpr FAny() { Invalidate(); } - constexpr TAny() { Storage.TypeInfo() = 0; } + FORCEINLINE constexpr FAny(FInvalid) : FAny() { } - constexpr TAny(FInvalid) : TAny() { } - - FORCEINLINE TAny(const TAny& InValue) + FORCEINLINE FAny(const FAny& InValue) + : TypeInfo(InValue.TypeInfo) { - Storage.CopyCustom(InValue.Storage); - - Storage.TypeInfo() = InValue.Storage.TypeInfo(); - if (!IsValid()) return; switch (GetRepresentation()) { + case ERepresentation::Empty: + break; case ERepresentation::Trivial: - Memory::Memcpy(Storage.InlineAllocation(), InValue.Storage.InlineAllocation(), Storage.InlineSize); + Memory::Memcpy(TrivialStorage.Internal, InValue.TrivialStorage.Internal); break; case ERepresentation::Small: - GetTypeInfoImpl().CopyConstructImpl(GetAllocation(), InValue.GetAllocation()); + SmallStorage.RTTI = InValue.SmallStorage.RTTI; + SmallStorage.RTTI->CopyConstruct(&SmallStorage.Internal, &InValue.SmallStorage.Internal); break; case ERepresentation::Big: - Storage.HeapAllocation() = Memory::Malloc(GetTypeInfoImpl().TypeSize, GetTypeInfoImpl().TypeAlignment); - GetTypeInfoImpl().CopyConstructImpl(GetAllocation(), InValue.GetAllocation()); + BigStorage.RTTI = InValue.BigStorage.RTTI; + BigStorage.External = Memory::Malloc(BigStorage.RTTI->TypeSize, BigStorage.RTTI->TypeAlignment); + BigStorage.RTTI->CopyConstruct(BigStorage.External, InValue.BigStorage.External); break; default: check_no_entry(); } } - FORCEINLINE TAny(TAny&& InValue) + FORCEINLINE FAny(FAny&& InValue) + : TypeInfo(InValue.TypeInfo) { - Storage.MoveCustom(MoveTemp(InValue.Storage)); - - Storage.TypeInfo() = InValue.Storage.TypeInfo(); - if (!IsValid()) return; switch (GetRepresentation()) { + case ERepresentation::Empty: + break; case ERepresentation::Trivial: - Memory::Memcpy(Storage.InlineAllocation(), InValue.Storage.InlineAllocation(), Storage.InlineSize); + Memory::Memmove(TrivialStorage.Internal, InValue.TrivialStorage.Internal); break; case ERepresentation::Small: - GetTypeInfoImpl().MoveConstructImpl(GetAllocation(), InValue.GetAllocation()); + SmallStorage.RTTI = InValue.SmallStorage.RTTI; + SmallStorage.RTTI->MoveConstruct(&SmallStorage.Internal, &InValue.SmallStorage.Internal); break; case ERepresentation::Big: - Storage.HeapAllocation() = InValue.Storage.HeapAllocation(); - InValue.Storage.TypeInfo() = 0; + BigStorage.RTTI = InValue.BigStorage.RTTI; + BigStorage.External = InValue.BigStorage.External; + InValue.Invalidate(); break; default: check_no_entry(); } @@ -136,28 +81,25 @@ public: template requires (CDestructible> && CConstructibleFrom, Ts&&...>) - FORCEINLINE explicit TAny(TInPlaceType, Ts&&... Args) + FORCEINLINE explicit FAny(TInPlaceType, Ts&&... Args) { - using SelectedType = TDecay; - EmplaceImpl(Forward(Args)...); + EmplaceImpl(Forward(Args)...); } - template requires (!CBaseOf> && !CTInPlaceType> + template requires (!CBaseOf> && !CTInPlaceType> && CDestructible> && CConstructibleFrom, T&&>) - FORCEINLINE TAny(T&& InValue) : TAny(InPlaceType>, Forward(InValue)) + FORCEINLINE FAny(T&& InValue) : FAny(InPlaceType>, Forward(InValue)) { } - FORCEINLINE ~TAny() + FORCEINLINE ~FAny() { - ResetImpl(); + Destroy(); } - FORCEINLINE TAny& operator=(const TAny& InValue) + FORCEINLINE FAny& operator=(const FAny& InValue) { if (&InValue == this) return *this; - Storage.CopyCustom(InValue.Storage); - if (!InValue.IsValid()) { Reset(); @@ -166,33 +108,43 @@ public: { switch (GetRepresentation()) { + case ERepresentation::Empty: + break; case ERepresentation::Trivial: - Memory::Memcpy(Storage.InlineAllocation(), InValue.Storage.InlineAllocation(), Storage.InlineSize); + Memory::Memcpy(TrivialStorage.Internal, InValue.TrivialStorage.Internal); break; case ERepresentation::Small: + SmallStorage.RTTI = InValue.SmallStorage.RTTI; + SmallStorage.RTTI->CopyAssign(&SmallStorage.Internal, &InValue.SmallStorage.Internal); + break; case ERepresentation::Big: - GetTypeInfoImpl().CopyAssignImpl(GetAllocation(), InValue.GetAllocation()); + BigStorage.RTTI = InValue.BigStorage.RTTI; + BigStorage.RTTI->CopyAssign(BigStorage.External, InValue.BigStorage.External); break; default: check_no_entry(); } } else { - ResetImpl(); + Destroy(); - Storage.TypeInfo() = InValue.Storage.TypeInfo(); + TypeInfo = InValue.TypeInfo; switch (GetRepresentation()) { + case ERepresentation::Empty: + break; case ERepresentation::Trivial: - Memory::Memcpy(Storage.InlineAllocation(), InValue.Storage.InlineAllocation(), Storage.InlineSize); + Memory::Memcpy(TrivialStorage.Internal, InValue.TrivialStorage.Internal); break; case ERepresentation::Small: - GetTypeInfoImpl().CopyConstructImpl(GetAllocation(), InValue.GetAllocation()); + SmallStorage.RTTI = InValue.SmallStorage.RTTI; + SmallStorage.RTTI->CopyConstruct(&SmallStorage.Internal, &InValue.SmallStorage.Internal); break; case ERepresentation::Big: - Storage.HeapAllocation() = Memory::Malloc(GetTypeInfoImpl().TypeSize, GetTypeInfoImpl().TypeAlignment); - GetTypeInfoImpl().CopyConstructImpl(GetAllocation(), InValue.GetAllocation()); + BigStorage.RTTI = InValue.BigStorage.RTTI; + BigStorage.External = Memory::Malloc(BigStorage.RTTI->TypeSize, BigStorage.RTTI->TypeAlignment); + BigStorage.RTTI->CopyConstruct(BigStorage.External, InValue.BigStorage.External); break; default: check_no_entry(); } @@ -201,12 +153,10 @@ public: return *this; } - FORCEINLINE TAny& operator=(TAny&& InValue) + FORCEINLINE FAny& operator=(FAny&& InValue) { if (&InValue == this) return *this; - Storage.MoveCustom(MoveTemp(InValue.Storage)); - if (!InValue.IsValid()) { Reset(); @@ -215,37 +165,45 @@ public: { switch (GetRepresentation()) { + case ERepresentation::Empty: + break; case ERepresentation::Trivial: - Memory::Memcpy(Storage.InlineAllocation(), InValue.Storage.InlineAllocation(), Storage.InlineSize); + Memory::Memmove(TrivialStorage.Internal, InValue.TrivialStorage.Internal); break; case ERepresentation::Small: - GetTypeInfoImpl().MoveAssignImpl(GetAllocation(), InValue.GetAllocation()); + SmallStorage.RTTI = InValue.SmallStorage.RTTI; + SmallStorage.RTTI->MoveAssign(&SmallStorage.Internal, &InValue.SmallStorage.Internal); break; case ERepresentation::Big: - ResetImpl(); - Storage.HeapAllocation() = InValue.Storage.HeapAllocation(); - InValue.Storage.TypeInfo() = 0; + Destroy(); + BigStorage.RTTI = InValue.BigStorage.RTTI; + BigStorage.External = InValue.BigStorage.External; + InValue.Invalidate(); break; default: check_no_entry(); } } else { - ResetImpl(); + Destroy(); - Storage.TypeInfo() = InValue.Storage.TypeInfo(); + TypeInfo = InValue.TypeInfo; switch (GetRepresentation()) { + case ERepresentation::Empty: + break; case ERepresentation::Trivial: - Memory::Memcpy(Storage.InlineAllocation(), InValue.Storage.InlineAllocation(), Storage.InlineSize); + Memory::Memmove(TrivialStorage.Internal, InValue.TrivialStorage.Internal); break; case ERepresentation::Small: - GetTypeInfoImpl().MoveConstructImpl(GetAllocation(), InValue.GetAllocation()); + SmallStorage.RTTI = InValue.SmallStorage.RTTI; + SmallStorage.RTTI->MoveConstruct(&SmallStorage.Internal, &InValue.SmallStorage.Internal); break; case ERepresentation::Big: - Storage.HeapAllocation() = InValue.Storage.HeapAllocation(); - InValue.Storage.TypeInfo() = 0; + BigStorage.RTTI = InValue.BigStorage.RTTI; + BigStorage.External = InValue.BigStorage.External; + InValue.Invalidate(); break; default: check_no_entry(); } @@ -254,20 +212,20 @@ public: return *this; } - template requires (!CBaseOf> && !CTInPlaceType> - && CDestructible> && CConstructibleFrom, T&&>) - FORCEINLINE TAny& operator=(T&& InValue) + template requires (!CBaseOf> && !CTInPlaceType> + && CDestructible>&& CConstructibleFrom, T&&>) + FORCEINLINE FAny& operator=(T&& InValue) { - using SelectedType = TDecay; + using DecayedType = TDecay; - if (HoldsAlternative()) + if (HoldsAlternative()) { - GetValue() = Forward(InValue); + GetValue() = Forward(InValue); } else { - ResetImpl(); - EmplaceImpl(Forward(InValue)); + Destroy(); + EmplaceImpl(Forward(InValue)); } return *this; @@ -277,58 +235,46 @@ public: && CConstructibleFrom, Ts&&...>) FORCEINLINE TDecay& Emplace(Ts&&... Args) { - ResetImpl(); - - using SelectedType = TDecay; - EmplaceImpl(Forward(Args)...); - return GetValue(); + Destroy(); + EmplaceImpl(Forward(Args)...); + return GetValue>(); } - constexpr const type_info& GetTypeInfo() const { return IsValid() ? *GetTypeInfoImpl().NativeTypeInfo : typeid(void); } + FORCEINLINE constexpr const type_info& GetTypeInfo() const { return IsValid() ? GetTypeInfoImpl() : typeid(void); } - constexpr bool IsValid() const { return Storage.TypeInfo() != 0; } - constexpr explicit operator bool() const { return Storage.TypeInfo() != 0; } + FORCEINLINE constexpr bool IsValid() const { return TypeInfo != 0; } + FORCEINLINE constexpr explicit operator bool() const { return TypeInfo != 0; } - template constexpr bool HoldsAlternative() const { return IsValid() ? GetTypeInfo() == typeid(T) : false; } + template FORCEINLINE constexpr bool HoldsAlternative() const { return IsValid() ? GetTypeInfo() == typeid(T) : false; } - template requires (CDestructible>) - 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*>(GetAllocation()); } - - template requires (CDestructible>) - 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*>(GetAllocation())); } - - template requires (CDestructible>) - 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(GetAllocation()); } - - template requires (CDestructible>) - 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(GetAllocation())); } + template requires (CSameAs>&& CDestructible>) + FORCEINLINE 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*>(GetStorage()); } + + template requires (CSameAs>&& CDestructible>) + FORCEINLINE 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*>(GetStorage())); } + + template requires (CSameAs>&& CDestructible>) + FORCEINLINE 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(GetStorage()); } + + template requires (CSameAs>&& CDestructible>) + FORCEINLINE 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(GetStorage())); } + + template requires (CSameAs> && CDestructible>) + FORCEINLINE constexpr T& Get( T& DefaultValue) & { return HoldsAlternative() ? GetValue() : DefaultValue; } template requires (CSameAs> && CDestructible>) - constexpr T& Get( T& DefaultValue) & { return HoldsAlternative() ? GetValue() : DefaultValue; } - - template requires (CSameAs> && CDestructible>) - constexpr const T& Get(const T& DefaultValue) const& { return HoldsAlternative() ? GetValue() : DefaultValue; } - - constexpr CustomStorage& GetCustomStorage() requires (!CSameAs) { return Storage; } - constexpr const CustomStorage& GetCustomStorage() const requires (!CSameAs) { return Storage; } + FORCEINLINE constexpr const T& Get(const T& DefaultValue) const& { return HoldsAlternative() ? GetValue() : DefaultValue; } FORCEINLINE void Reset() { - ResetImpl(); - Storage.TypeInfo() = 0; + Destroy(); + Invalidate(); } - - FORCEINLINE size_t GetTypeHash() const - { - using NAMESPACE_REDCRAFT::GetTypeHash; - if (!IsValid()) return 20090007; - return HashCombine(GetTypeHash(GetTypeInfo()), GetTypeInfoImpl().HashImpl(GetAllocation())); - } - - FORCEINLINE void Swap(TAny& InValue) + + FORCEINLINE void Swap(FAny& InValue) { if (!IsValid() && !InValue.IsValid()) return; - + if (IsValid() && !InValue.IsValid()) { InValue = MoveTemp(*this); @@ -342,173 +288,256 @@ public: InValue.Reset(); return; } - + if (GetTypeInfo() == InValue.GetTypeInfo()) { - GetTypeInfoImpl().SwapImpl(GetAllocation(), InValue.GetAllocation()); + switch (GetRepresentation()) + { + case ERepresentation::Empty: + break; + case ERepresentation::Trivial: + uint8 Buffer[sizeof(TrivialStorage.Internal)]; + Memory::Memmove(Buffer, TrivialStorage.Internal); + Memory::Memmove(TrivialStorage.Internal, InValue.TrivialStorage.Internal); + Memory::Memmove(InValue.TrivialStorage.Internal, Buffer); + break; + case ERepresentation::Small: + SmallStorage.RTTI->SwapObject(&SmallStorage.Internal, &InValue.SmallStorage.Internal); + break; + case ERepresentation::Big: + NAMESPACE_REDCRAFT::Swap(BigStorage.External, InValue.BigStorage.External); + break; + default: check_no_entry(); + } + return; } - TAny Temp = MoveTemp(*this); + FAny Temp = MoveTemp(*this); *this = MoveTemp(InValue); InValue = MoveTemp(Temp); } private: - CustomStorage Storage; - - static constexpr uintptr_t RepresentationMask = 3; - - enum class ERepresentation : uint8 + struct FRTTI { - Trivial, // Trivial & Inline - Small, // InlineAllocation - Big, // HeapAllocation - }; - - struct FTypeInfoImpl - { - const type_info* NativeTypeInfo; - const size_t TypeSize; const size_t TypeAlignment; - using FCopyConstructImpl = void(*)(void*, const void*); - using FMoveConstructImpl = void(*)(void*, void*); - using FCopyAssignImpl = void(*)(void*, const void*); - using FMoveAssignImpl = void(*)(void*, void*); - using FDestroyImpl = void(*)(void* ); - - using FEqualityCompareImpl = bool (*)(const void*, const void*); - using FSynthThreeWayCompareImpl = partial_ordering (*)(const void*, const void*); - using FHashImpl = size_t (*)(const void* ); - using FSwapImpl = void (*)( void*, void*); - - const FCopyConstructImpl CopyConstructImpl; - const FMoveConstructImpl MoveConstructImpl; - const FCopyAssignImpl CopyAssignImpl; - const FMoveAssignImpl MoveAssignImpl; - const FDestroyImpl DestroyImpl; + using FCopyConstruct = void(*)(void*, const void*); + using FMoveConstruct = void(*)(void*, void*); + using FCopyAssign = void(*)(void*, const void*); + using FMoveAssign = void(*)(void*, void*); + using FDestruct = void(*)(void* ); + using FSwapObject = void(*)(void*, void*); - const FEqualityCompareImpl EqualityCompareImpl; - const FSynthThreeWayCompareImpl SynthThreeWayCompareImpl; - const FHashImpl HashImpl; - const FSwapImpl SwapImpl; + const FCopyConstruct CopyConstruct; + const FMoveConstruct MoveConstruct; + const FCopyAssign CopyAssign; + const FMoveAssign MoveAssign; + const FDestruct Destruct; + const FSwapObject SwapObject; template - constexpr FTypeInfoImpl(TInPlaceType) - - : NativeTypeInfo (&typeid(T)) - , TypeSize ( sizeof(T)) - , TypeAlignment (alignof(T)) - - , CopyConstructImpl ([](void* A, const void* B) { if constexpr (requires(T* A, const T* B) { Memory::CopyConstruct (A, B); }) Memory::CopyConstruct (reinterpret_cast(A), reinterpret_cast(B)); else checkf(false, TEXT("The type '%s' is not copy constructible."), typeid(T).name()); }) - , MoveConstructImpl ([](void* A, void* B) { if constexpr (requires(T* A, T* B) { Memory::MoveConstruct (A, B); }) Memory::MoveConstruct (reinterpret_cast(A), reinterpret_cast< T*>(B)); else checkf(false, TEXT("The type '%s' is not move constructible."), typeid(T).name()); }) - , CopyAssignImpl ([](void* A, const void* B) { if constexpr (requires(T* A, const T* B) { Memory::CopyAssign (A, B); }) Memory::CopyAssign (reinterpret_cast(A), reinterpret_cast(B)); else checkf(false, TEXT("The type '%s' is not copy assignable."), typeid(T).name()); }) - , MoveAssignImpl ([](void* A, void* B) { if constexpr (requires(T* A, T* B) { Memory::MoveAssign (A, B); }) Memory::MoveAssign (reinterpret_cast(A), reinterpret_cast< T*>(B)); else checkf(false, TEXT("The type '%s' is not move assignable."), typeid(T).name()); }) - , DestroyImpl ([](void* A ) { if constexpr (requires(T* A ) { Memory::Destruct (A ); }) Memory::Destruct (reinterpret_cast(A) ); else checkf(false, TEXT("The type '%s' is not destructible."), typeid(T).name()); }) - - , EqualityCompareImpl ([](const void* A, const void* B) -> bool { if constexpr (CEqualityComparable ) return (*reinterpret_cast(A) == *reinterpret_cast(B)); else checkf(false, TEXT("The type '%s' is not equality comparable."), typeid(T).name()); return false; }) - , SynthThreeWayCompareImpl ([](const void* A, const void* B) -> partial_ordering { if constexpr (CSynthThreeWayComparable) return NAMESPACE_REDCRAFT::SynthThreeWayCompare (*reinterpret_cast(A), *reinterpret_cast(B)); else checkf(false, TEXT("The type '%s' is not synth three-way comparable."), typeid(T).name()); return partial_ordering::unordered; }) - , HashImpl ([](const void* A ) -> size_t { if constexpr (CHashable ) return NAMESPACE_REDCRAFT::GetTypeHash (*reinterpret_cast(A) ); else checkf(false, TEXT("The type '%s' is not hashable."), typeid(T).name()); return 1080551797; }) - , SwapImpl ([]( void* A, void* B) -> void { if constexpr (CSwappable ) NAMESPACE_REDCRAFT::Swap (*reinterpret_cast< T*>(A), *reinterpret_cast< T*>(B)); else checkf(false, TEXT("The type '%s' is not swappable."), typeid(T).name()); }) - + FORCEINLINE constexpr FRTTI(TInPlaceType) + : TypeSize( sizeof(T)), TypeAlignment(alignof(T)) + , CopyConstruct( + [](void* A, const void* B) + { + new (A) T(*reinterpret_cast(B)); + } + ) + , MoveConstruct( + [](void* A, void* B) + { + new (A) T(MoveTemp(*reinterpret_cast(B))); + } + ) + , CopyAssign( + [](void* A, const void* B) + { + if constexpr (CCopyAssignable) + { + *reinterpret_cast(A) = *reinterpret_cast(B); + } + else + { + reinterpret_cast(A)->~T(); + new (A) T(*reinterpret_cast(B)); + } + } + ) + , MoveAssign( + [](void* A, void* B) + { + if constexpr (CMoveAssignable) + { + *reinterpret_cast(A) = MoveTemp(*reinterpret_cast(B)); + } + else + { + reinterpret_cast(A)->~T(); + new (A) T(MoveTemp(*reinterpret_cast(B))); + } + } + ) + , Destruct( + [](void* A) + { + reinterpret_cast(A)->~T(); + } + ) + , SwapObject{ + [](void* A, void* B) + { + NAMESPACE_REDCRAFT::Swap(*reinterpret_cast(A), *reinterpret_cast(B)); + } + } { } }; - constexpr ERepresentation GetRepresentation() const { return static_cast(Storage.TypeInfo() & RepresentationMask); } - constexpr const FTypeInfoImpl& GetTypeInfoImpl() const { return *reinterpret_cast(Storage.TypeInfo() & ~RepresentationMask); } + struct FTrivialStorage + { + uint8 Internal[64 - sizeof(uintptr)]; + }; - constexpr void* GetAllocation() { return GetRepresentation() == ERepresentation::Trivial || GetRepresentation() == ERepresentation::Small ? Storage.InlineAllocation() : Storage.HeapAllocation(); } - constexpr const void* GetAllocation() const { return GetRepresentation() == ERepresentation::Trivial || GetRepresentation() == ERepresentation::Small ? Storage.InlineAllocation() : Storage.HeapAllocation(); } + struct FSmallStorage + { + uint8 Internal[sizeof(FTrivialStorage) - sizeof(const FRTTI*)]; + const FRTTI* RTTI; + }; - template + struct FBigStorage + { + uint8 Padding[sizeof(FTrivialStorage) - sizeof(void*) - sizeof(const FRTTI*)]; + void* External; + const FRTTI* RTTI; + }; + + static_assert(sizeof(FTrivialStorage) == sizeof(FSmallStorage)); + static_assert(sizeof(FTrivialStorage) == sizeof( FBigStorage)); + + static_assert(alignof(type_info) >= 4); + + static constexpr uintptr_t RepresentationMask = 3; + + enum class ERepresentation : uintptr + { + Empty = 0, // EmptyType + Trivial = 1, // TrivialStorage + Small = 2, // SmallStorage + Big = 3, // BigStorage + }; + + union + { + FTrivialStorage TrivialStorage; + FSmallStorage SmallStorage; + FBigStorage BigStorage; + }; + + uintptr TypeInfo; + + FORCEINLINE ERepresentation GetRepresentation() const { return static_cast(TypeInfo & RepresentationMask); } + FORCEINLINE const type_info& GetTypeInfoImpl() const { return *reinterpret_cast(TypeInfo & ~RepresentationMask); } + + FORCEINLINE void* GetStorage() + { + switch (GetRepresentation()) + { + case ERepresentation::Empty: return nullptr; + case ERepresentation::Trivial: return &TrivialStorage.Internal; + case ERepresentation::Small: return &SmallStorage.Internal; + case ERepresentation::Big: return BigStorage.External; + default: check_no_entry(); return nullptr; + } + } + + FORCEINLINE const void* GetStorage() const + { + switch (GetRepresentation()) + { + case ERepresentation::Empty: return nullptr; + case ERepresentation::Trivial: return &TrivialStorage.Internal; + case ERepresentation::Small: return &SmallStorage.Internal; + case ERepresentation::Big: return BigStorage.External; + default: check_no_entry(); return nullptr; + } + } + + template FORCEINLINE void EmplaceImpl(Ts&&... Args) { - static constexpr const FTypeInfoImpl SelectedTypeInfo(InPlaceType); - Storage.TypeInfo() = reinterpret_cast(&SelectedTypeInfo); + using DecayedType = TDecay; - constexpr bool bIsInlineStorable = sizeof(SelectedType) <= Storage.InlineSize && alignof(SelectedType) <= Storage.InlineAlignment; - constexpr bool bIsTriviallyStorable = bIsInlineStorable && CTrivial && CTriviallyCopyable; + TypeInfo = reinterpret_cast(&typeid(DecayedType)); + + if constexpr (CEmpty && CTrivial) return; // ERepresentation::Empty + + constexpr bool bIsTriviallyStorable = sizeof(DecayedType) <= sizeof(TrivialStorage.Internal) && alignof(DecayedType) <= alignof(FAny) && CTriviallyCopyable; + constexpr bool bIsSmallStorable = sizeof(DecayedType) <= sizeof( SmallStorage.Internal) && alignof(DecayedType) <= alignof(FAny); + + static constexpr const FRTTI SelectedRTTI(InPlaceType); if constexpr (bIsTriviallyStorable) { - new(Storage.InlineAllocation()) SelectedType(Forward(Args)...); - Storage.TypeInfo() |= static_cast(ERepresentation::Trivial); + new (&TrivialStorage.Internal) DecayedType(Forward(Args)...); + TypeInfo |= static_cast(ERepresentation::Trivial); } - else if constexpr (bIsInlineStorable) + else if constexpr (bIsSmallStorable) { - new(Storage.InlineAllocation()) SelectedType(Forward(Args)...); - Storage.TypeInfo() |= static_cast(ERepresentation::Small); + new (&SmallStorage.Internal) DecayedType(Forward(Args)...); + SmallStorage.RTTI = &SelectedRTTI; + TypeInfo |= static_cast(ERepresentation::Small); } else { - Storage.HeapAllocation() = new SelectedType(Forward(Args)...); - Storage.TypeInfo() |= static_cast(ERepresentation::Big); + BigStorage.External = Memory::Malloc(sizeof(DecayedType), alignof(DecayedType)); + new (BigStorage.External) DecayedType(Forward(Args)...); + BigStorage.RTTI = &SelectedRTTI; + TypeInfo |= static_cast(ERepresentation::Big); } } - FORCEINLINE void ResetImpl() + FORCEINLINE void Destroy() { if (!IsValid()) return; switch (GetRepresentation()) { + case ERepresentation::Empty: case ERepresentation::Trivial: break; case ERepresentation::Small: - GetTypeInfoImpl().DestroyImpl(GetAllocation()); + SmallStorage.RTTI->Destruct(&SmallStorage.Internal); break; case ERepresentation::Big: - GetTypeInfoImpl().DestroyImpl(GetAllocation()); - Memory::Free(Storage.HeapAllocation()); + BigStorage.RTTI->Destruct(BigStorage.External); + Memory::Free(BigStorage.External); break; default: check_no_entry(); } } - friend FORCEINLINE bool operator==(const TAny& LHS, const TAny& RHS) + FORCEINLINE constexpr void Invalidate() { TypeInfo = 0; } + + template requires (!CBaseOf>) + friend FORCEINLINE constexpr bool operator==(const FAny& LHS, const T& RHS) { - if (LHS.GetTypeInfo() != RHS.GetTypeInfo()) return false; - if (LHS.IsValid() == false) return true; - return LHS.GetTypeInfoImpl().EqualityCompareImpl(LHS.GetAllocation(), RHS.GetAllocation()); + return LHS.template HoldsAlternative() ? LHS.template GetValue() == RHS : false; } - friend FORCEINLINE partial_ordering operator<=>(const TAny& LHS, const TAny& RHS) + friend FORCEINLINE constexpr bool operator==(const FAny& LHS, FInvalid) { - if (LHS.GetTypeInfo() != RHS.GetTypeInfo()) return partial_ordering::unordered; - if (LHS.IsValid() == false) return partial_ordering::equivalent; - return LHS.GetTypeInfoImpl().SynthThreeWayCompareImpl(LHS.GetAllocation(), RHS.GetAllocation());; + return !LHS.IsValid(); } }; -class FAny : STRONG_INHERIT(TAny); - static_assert(sizeof(FAny) == 64, "The byte size of FAny is unexpected"); -template requires (!CBaseOf>) -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(); -} - -NAMESPACE_PRIVATE_BEGIN - -template struct TIsTAny : FFalse { }; -template struct TIsTAny> : FTrue { }; - -NAMESPACE_PRIVATE_END - -template -concept CTAny = NAMESPACE_PRIVATE::TIsTAny>::Value; +static_assert(alignof(FAny) == 16, "The byte alignment of FAny is unexpected"); NAMESPACE_MODULE_END(Utility) NAMESPACE_MODULE_END(Redcraft) diff --git a/Redcraft.Utility/Source/Public/Templates/Function.h b/Redcraft.Utility/Source/Public/Templates/Function.h index 049d629..05678a5 100644 --- a/Redcraft.Utility/Source/Public/Templates/Function.h +++ b/Redcraft.Utility/Source/Public/Templates/Function.h @@ -321,15 +321,6 @@ public: private: - union - { - uint8 InternalStorage[64 - sizeof(uintptr) - sizeof(uintptr)]; - void* ExternalStorage; - }; - - uintptr RTTI; - uintptr Callable; - struct FMovableRTTI { const size_t TypeSize; @@ -390,6 +381,15 @@ private: Small = 2, // InternalStorage Big = 3, // ExternalStorage }; + + union + { + uint8 InternalStorage[64 - sizeof(uintptr) - sizeof(uintptr)]; + void* ExternalStorage; + }; + + uintptr RTTI; + uintptr Callable; FORCEINLINE constexpr ERepresentation GetRepresentation() const { return static_cast(RTTI & RepresentationMask); } FORCEINLINE constexpr const FRTTI& GetRTTI() const { return *reinterpret_cast(RTTI & ~RepresentationMask); }