diff --git a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp index 999dee3..e5eab1a 100644 --- a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp +++ b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp @@ -1101,9 +1101,6 @@ void TestFunction() always_check(TempC() == 0xEE); always_check(TempD() == 0xFF); - - always_check(TempC.TargetType() == typeid(FFunctor)); - always_check(TempD.TargetType() == typeid(FFunctor)); } { @@ -1127,11 +1124,11 @@ void TestFunction() // TFunction ObjectE = MoveTemp(RefA); // TUniqueFunction UniqueE = MoveTemp(RefA); -// TFunctionRef RefF = MoveTemp(ObjectA); + TFunctionRef RefF = MoveTemp(ObjectA); TFunction ObjectF = MoveTemp(ObjectA); TUniqueFunction UniqueF = MoveTemp(ObjectA); -// TFunctionRef RefG = MoveTemp(UniqueA); + TFunctionRef RefG = MoveTemp(UniqueA); // TFunction ObjectG = MoveTemp(UniqueA); TUniqueFunction UniqueG = MoveTemp(UniqueA); } diff --git a/Redcraft.Utility/Source/Public/Templates/Function.h b/Redcraft.Utility/Source/Public/Templates/Function.h index 8949180..04bef52 100644 --- a/Redcraft.Utility/Source/Public/Templates/Function.h +++ b/Redcraft.Utility/Source/Public/Templates/Function.h @@ -1,14 +1,19 @@ #pragma once #include "CoreTypes.h" -#include "Templates/Any.h" +#include "Memory/Memory.h" #include "Templates/Meta.h" #include "Templates/Invoke.h" -#include "Memory/Alignment.h" #include "Templates/Utility.h" #include "TypeTraits/TypeTraits.h" #include "Miscellaneous/AssertionMacros.h" +// 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. +// But we don't follow the the copy-and-swap idiom, see "Templates/Any.h". +// This class implements assignment operations in a way that assumes no assignment operations of the type, +// because the assignment operations of TFunction are in most cases different between LHS and RHS. + NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) @@ -41,6 +46,359 @@ template concept CTUniqueFunction = NAMESPACE_PRIVATE::TIsTUniqueFu NAMESPACE_PRIVATE_BEGIN +template +class TFunctionStorage; + +template +class TFunctionStorage +{ +public: + + constexpr TFunctionStorage() = default; + constexpr TFunctionStorage(const TFunctionStorage&) = default; + constexpr TFunctionStorage(TFunctionStorage&&) = default; + constexpr TFunctionStorage& operator=(const TFunctionStorage&) = delete; + constexpr TFunctionStorage& operator=(TFunctionStorage&&) = delete; + constexpr ~TFunctionStorage() = default; + + constexpr uintptr GetValuePtr() const { return ValuePtr; } + constexpr uintptr GetCallable() const { return Callable; } + + constexpr bool IsValid() const { return ValuePtr != 0; } + + // Use Invalidate() to invalidate the storage or use Emplace() to emplace a new object after destruction. + constexpr void Destroy() { } + + // Make sure you call this function after you have destroyed the held object using Destroy(). + constexpr void Invalidate() { ValuePtr = 0; } + + // Make sure you call this function after you have destroyed the held object using Destroy(). + template + constexpr void Emplace(intptr InCallable, U&& Args) + { + static_assert(CSameAs, TDecay>); + ValuePtr = reinterpret_cast(AddressOf(Args)); + Callable = InCallable; + } + + constexpr void Swap(TFunctionStorage& InValue) + { + NAMESPACE_REDCRAFT::Swap(ValuePtr, InValue.ValuePtr); + NAMESPACE_REDCRAFT::Swap(Callable, InValue.Callable); + } + +private: + + uintptr ValuePtr; + uintptr Callable; + +}; + +// For non-unique storage, the memory layout should be compatible with unique storage, +// i.e. it can be directly reinterpreted_cast. +template +class alignas(16) TFunctionStorage +{ +public: + + constexpr TFunctionStorage() = default; + + FORCEINLINE TFunctionStorage(const TFunctionStorage& InValue) requires (!bIsUnique) + : TypeInfo(InValue.TypeInfo) + { + if (!IsValid()) return; + + Callable = InValue.Callable; + + switch (GetRepresentation()) + { + case ERepresentation::Empty: + break; + case ERepresentation::Trivial: + Memory::Memcpy(InternalStorage, InValue.InternalStorage); + break; + case ERepresentation::Small: + GetTypeInfo().CopyConstruct(GetStorage(), InValue.GetStorage()); + break; + case ERepresentation::Big: + ExternalStorage = Memory::Malloc(GetTypeInfo().TypeSize, GetTypeInfo().TypeAlignment); + GetTypeInfo().CopyConstruct(GetStorage(), InValue.GetStorage()); + break; + default: check_no_entry(); + } + } + + FORCEINLINE TFunctionStorage(TFunctionStorage&& InValue) + : TypeInfo(InValue.TypeInfo) + { + if (!IsValid()) return; + + Callable = InValue.Callable; + + switch (GetRepresentation()) + { + case ERepresentation::Empty: + break; + case ERepresentation::Trivial: + Memory::Memcpy(InternalStorage, InValue.InternalStorage); + break; + case ERepresentation::Small: + GetTypeInfo().MoveConstruct(GetStorage(), InValue.GetStorage()); + break; + case ERepresentation::Big: + ExternalStorage = InValue.ExternalStorage; + InValue.Invalidate(); + break; + default: check_no_entry(); + } + } + + FORCEINLINE ~TFunctionStorage() + { + Destroy(); + } + + FORCEINLINE TFunctionStorage& operator=(const TFunctionStorage& InValue) requires (!bIsUnique) + { + if (&InValue == this) return *this; + + if (!InValue.IsValid()) + { + Destroy(); + Invalidate(); + } + else + { + Destroy(); + + TypeInfo = InValue.TypeInfo; + Callable = InValue.Callable; + + switch (GetRepresentation()) + { + case ERepresentation::Empty: + break; + case ERepresentation::Trivial: + Memory::Memcpy(InternalStorage, InValue.InternalStorage); + break; + case ERepresentation::Small: + GetTypeInfo().CopyConstruct(GetStorage(), InValue.GetStorage()); + break; + case ERepresentation::Big: + ExternalStorage = Memory::Malloc(GetTypeInfo().TypeSize, GetTypeInfo().TypeAlignment); + GetTypeInfo().CopyConstruct(GetStorage(), InValue.GetStorage()); + break; + default: check_no_entry(); + } + } + + return *this; + } + + FORCEINLINE TFunctionStorage& operator=(TFunctionStorage&& InValue) + { + if (&InValue == this) return *this; + + if (!InValue.IsValid()) + { + Destroy(); + Invalidate(); + } + else + { + Destroy(); + + TypeInfo = InValue.TypeInfo; + Callable = InValue.Callable; + + switch (GetRepresentation()) + { + case ERepresentation::Empty: + break; + case ERepresentation::Trivial: + Memory::Memcpy(InternalStorage, InValue.InternalStorage); + break; + case ERepresentation::Small: + GetTypeInfo().MoveConstruct(GetStorage(), InValue.GetStorage()); + break; + case ERepresentation::Big: + ExternalStorage = InValue.ExternalStorage; + InValue.Invalidate(); + break; + default: check_no_entry(); + } + } + + return *this; + } + + constexpr uintptr GetValuePtr() const { return reinterpret_cast(GetStorage()); } + constexpr uintptr GetCallable() const { return Callable; } + + constexpr bool IsValid() const { return TypeInfo != 0; } + + // Use Invalidate() to invalidate the storage or use Emplace() to emplace a new object after destruction. + FORCEINLINE void Destroy() + { + if (!IsValid()) return; + + switch (GetRepresentation()) + { + case ERepresentation::Empty: + case ERepresentation::Trivial: + break; + case ERepresentation::Small: + GetTypeInfo().Destruct(GetStorage()); + break; + case ERepresentation::Big: + GetTypeInfo().Destruct(GetStorage()); + Memory::Free(ExternalStorage); + break; + default: check_no_entry(); + } + } + + // Make sure you call this function after you have destroyed the held object using Destroy(). + constexpr void Invalidate() { TypeInfo = 0; } + + // Make sure you call this function after you have destroyed the held object using Destroy(). + template + FORCEINLINE void Emplace(uintptr InCallable, Ts&&... Args) + { + Callable = InCallable; + + using DecayedType = TDecay; + + static constexpr const FTypeInfo SelectedTypeInfo(InPlaceType); + TypeInfo = reinterpret_cast(&SelectedTypeInfo); + + if constexpr (CEmpty) return; + + constexpr bool bIsInlineStorable = sizeof(DecayedType) <= sizeof(InternalStorage) && alignof(DecayedType) <= alignof(TFunctionStorage); + constexpr bool bIsTriviallyStorable = bIsInlineStorable && CTrivial && CTriviallyCopyable; + + if constexpr (bIsTriviallyStorable) + { + new (&InternalStorage) DecayedType(Forward(Args)...); + TypeInfo |= static_cast(ERepresentation::Trivial); + } + else if constexpr (bIsInlineStorable) + { + new (&InternalStorage) DecayedType(Forward(Args)...); + TypeInfo |= static_cast(ERepresentation::Small); + } + else + { + ExternalStorage = new DecayedType(Forward(Args)...); + TypeInfo |= static_cast(ERepresentation::Big); + } + + } + + FORCEINLINE void Swap(TFunctionStorage& InValue) + { + if (!IsValid() && !InValue.IsValid()) return; + + if (IsValid() && !InValue.IsValid()) + { + InValue = MoveTemp(*this); + Destroy(); + Invalidate(); + } + else if (InValue.IsValid() && !IsValid()) + { + *this = MoveTemp(InValue); + InValue.Destroy(); + InValue.Invalidate(); + } + else + { + TFunctionStorage Temp = MoveTemp(*this); + *this = MoveTemp(InValue); + InValue = MoveTemp(Temp); + } + } + +private: + + union + { + uint8 InternalStorage[64 - sizeof(uintptr) - sizeof(uintptr)]; + void* ExternalStorage; + }; + + uintptr TypeInfo; + uintptr Callable; + + struct FMovableTypeInfo + { + const size_t TypeSize; + const size_t TypeAlignment; + + using FMoveConstruct = void(*)(void*, void*); + using FDestruct = void(*)(void* ); + + const FMoveConstruct MoveConstruct; + const FDestruct Destruct; + + template + constexpr FMovableTypeInfo(TInPlaceType) + : TypeSize(sizeof(T)), TypeAlignment(alignof(T)) + , MoveConstruct( + [](void* A, void* B) + { + new (A) T(*reinterpret_cast(B)); + } + ) + , Destruct( + [](void* A) + { + reinterpret_cast(A)->~T(); + } + ) + { } + }; + + struct FCopyableTypeInfo : public FMovableTypeInfo + { + using FCopyConstruct = void(*)(void*, const void*); + + const FCopyConstruct CopyConstruct; + + template + constexpr FCopyableTypeInfo(TInPlaceType) + : FMovableTypeInfo(InPlaceType) + , CopyConstruct( + [](void* A, const void* B) + { + new (A) T(*reinterpret_cast(B)); + } + ) + { } + }; + + using FTypeInfo = TConditional; + + static_assert(alignof(FTypeInfo) >= 4); + + static constexpr uintptr_t RepresentationMask = 3; + + enum class ERepresentation : uintptr + { + Empty = 0, // EmptyType + Trivial = 1, // Trivial & Internal + Small = 2, // InternalStorage + Big = 3, // ExternalStorage + }; + + constexpr ERepresentation GetRepresentation() const { return static_cast(TypeInfo & RepresentationMask); } + constexpr const FTypeInfo& GetTypeInfo() const { return *reinterpret_cast(TypeInfo & ~RepresentationMask); } + + constexpr void* GetStorage() { return GetRepresentation() == ERepresentation::Trivial || GetRepresentation() == ERepresentation::Small ? InternalStorage : ExternalStorage; } + constexpr const void* GetStorage() const { return GetRepresentation() == ERepresentation::Trivial || GetRepresentation() == ERepresentation::Small ? InternalStorage : ExternalStorage; } + +}; + template constexpr bool FunctionIsBound(const T& Func) { @@ -72,7 +430,7 @@ struct TIsInvocableSignature template struct TIsInvocableSignature : TBoolConstant> { }; template struct TIsInvocableSignature : TBoolConstant> { }; -template struct TFunctionInfo; +template struct TFunctionInfo; template struct TFunctionInfo { using Fn = Ret(Ts...); using CVRef = int; }; template struct TFunctionInfo { using Fn = Ret(Ts...); using CVRef = int&; }; template struct TFunctionInfo { using Fn = Ret(Ts...); using CVRef = int&&; }; @@ -80,22 +438,22 @@ template struct TFunctionInfo template struct TFunctionInfo { using Fn = Ret(Ts...); using CVRef = const int&; }; template struct TFunctionInfo { using Fn = Ret(Ts...); using CVRef = const int&&; }; -template class TFunctionImpl; +template class TFunctionImpl; -template -class TFunctionImpl +template +class TFunctionImpl { public: using ResultType = Ret; using ArgumentType = TTypeSequence; - TFunctionImpl() = default; - TFunctionImpl(const TFunctionImpl&) = default; - TFunctionImpl(TFunctionImpl&& InValue) = default; - TFunctionImpl& operator=(const TFunctionImpl&) = default; - TFunctionImpl& operator=(TFunctionImpl&&) = default; - ~TFunctionImpl() = default; + constexpr TFunctionImpl() = default; + constexpr TFunctionImpl(const TFunctionImpl&) = default; + constexpr TFunctionImpl(TFunctionImpl&&) = default; + constexpr TFunctionImpl& operator=(const TFunctionImpl&) = default; + constexpr TFunctionImpl& operator=(TFunctionImpl&&) = default; + constexpr ~TFunctionImpl() = default; FORCEINLINE ResultType operator()(Ts... Args) requires (CSameAs) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Ts... Args) & requires (CSameAs) { return CallImpl(Forward(Args)...); } @@ -104,155 +462,58 @@ public: FORCEINLINE ResultType operator()(Ts... Args) const& requires (CSameAs) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Ts... Args) const&& requires (CSameAs) { return CallImpl(Forward(Args)...); } - constexpr bool IsValid() const { return GetCallableImpl() != nullptr; } - constexpr explicit operator bool() const { return GetCallableImpl() != nullptr; } + constexpr bool IsValid() const { return Storage.IsValid(); } + constexpr explicit operator bool() const { return Storage.IsValid(); } - FORCEINLINE const type_info& TargetType() const requires (!bIsRef) { return IsValid() ? Storage.GetTypeInfo() : typeid(void); }; - - template FORCEINLINE T& Target() & requires (!bIsRef && CDestructible>) { return static_cast< StorageType& >(Storage).template GetValue(); } - template FORCEINLINE T&& Target() && requires (!bIsRef && CDestructible>) { return static_cast< StorageType&&>(Storage).template GetValue(); } - template FORCEINLINE const T& Target() const& requires (!bIsRef && CDestructible>) { return static_cast(Storage).template GetValue(); } - template FORCEINLINE const T&& Target() const&& requires (!bIsRef && CDestructible>) { return static_cast(Storage).template GetValue(); } - - constexpr void Swap(TFunctionImpl& InValue) requires (!bIsRef) - { - using NAMESPACE_REDCRAFT::Swap; - - if (!IsValid() && !InValue.IsValid()) return; - - if (IsValid() && !InValue.IsValid()) - { - InValue = MoveTemp(*this); - ResetImpl(); - return; - } - - if (InValue.IsValid() && !IsValid()) - { - *this = MoveTemp(InValue); - InValue.ResetImpl(); - return; - } - - Swap(Storage, InValue.Storage); - } + constexpr void Swap(TFunctionImpl& InValue) { Storage.Swap(InValue.Storage); } private: - using StoragePtrType = TCopyConst*; - using CallableType = ResultType(*)(StoragePtrType, Ts&&...); + using CallableType = ResultType(*)(uintptr, Ts&&...); - struct FunctionRefStorage - { - StoragePtrType Ptr; - CallableType Callable; - }; - - template - struct alignas(16) FFunctionStorage : FSingleton - { - //~ Begin CAnyCustomStorage Interface - inline static constexpr size_t InlineSize = 64 - sizeof(uintptr) - sizeof(CallableType); - 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 FFunctionStorage& InValue) { Callable = InValue.Callable; } - constexpr void MoveCustom( FFunctionStorage&& InValue) { Callable = InValue.Callable; } - //~ End CAnyCustomStorage Interface - - union - { - uint8 InlineAllocationImpl[InlineSize]; - void* HeapAllocationImpl; - }; - - uintptr TypeInfoImpl; - - CallableType Callable; - - }; - - using FunctionStorage = TAny>; - using StorageType = TConditional; - - StorageType Storage; - - FORCEINLINE CallableType& GetCallableImpl() - { - if constexpr (bIsRef) return Storage.Callable; - else return Storage.GetCustomStorage().Callable; - } - - FORCEINLINE CallableType GetCallableImpl() const - { - if constexpr (bIsRef) return Storage.Callable; - else return Storage.GetCustomStorage().Callable; - } - - FORCEINLINE ResultType CallImpl(Ts&&... Args) - { - checkf(IsValid(), TEXT("Attempting to call an unbound TFunction!")); - if constexpr (bIsRef) return GetCallableImpl()(Storage.Ptr, Forward(Args)...); - else return GetCallableImpl()(&Storage, Forward(Args)...); - } + TFunctionStorage Storage; FORCEINLINE ResultType CallImpl(Ts&&... Args) const { checkf(IsValid(), TEXT("Attempting to call an unbound TFunction!")); - if constexpr (bIsRef) return GetCallableImpl()(Storage.Ptr, Forward(Args)...); - else return GetCallableImpl()(&Storage, Forward(Args)...); + CallableType Callable = reinterpret_cast(Storage.GetCallable()); + return Callable(Storage.GetValuePtr(), Forward(Args)...); } protected: // These functions should not be used by user-defined class - template - FORCEINLINE void EmplaceImpl(ArgTypes&&... Args) + // Use Invalidate() to invalidate the storage or use Emplace() to emplace a new object after destruction. + FORCEINLINE void Destroy() { Storage.Destroy(); } + + // Make sure you call this function after you have destroyed the held object using Destroy(). + constexpr void Invalidate() { Storage.Invalidate(); } + + // Make sure you call this function after you have destroyed the held object using Destroy(). + template + FORCEINLINE TDecay& Emplace(ArgTypes&&... Args) { - using FuncType = TCopyConst, DecayedType>; + using DecayedType = TDecay; - if constexpr (bIsRef) Storage.Ptr = (AddressOf(Args), ...); - else Storage.template Emplace(Forward(Args)...); + // This add a l-value reference to a non-reference type, while preserving the r-value reference. + using ObjectType = TCopyCVRef; + using InvokeType = TConditional, ObjectType, ObjectType&>; - GetCallableImpl() = [](StoragePtrType Storage, Ts&&... Args) -> ResultType + CallableType Callable = [](uintptr ObjectPtr, Ts&&... Args) -> ResultType { - using InvokeType = TConditional< - CReference, - TCopyCVRef, - TCopyCVRef& - >; - - const auto GetFunc = [Storage]() -> InvokeType - { - if constexpr (!bIsRef) return static_cast*>(Storage)->template GetValue(); - else return static_cast(*reinterpret_cast(Storage)); - }; - - return InvokeResult(GetFunc(), Forward(Args)...); + return InvokeResult( + static_cast(*reinterpret_cast(ObjectPtr)), + Forward(Args)... + ); }; - } - FORCEINLINE void AssignImpl(const TFunctionImpl& InValue) - { - if (InValue.IsValid()) Storage = InValue.Storage; - else ResetImpl(); - } + Storage.template Emplace( + reinterpret_cast(Callable), + Forward(Args)... + ); - FORCEINLINE void AssignImpl(TFunctionImpl&& InValue) - { - if (InValue.IsValid()) - { - Storage = MoveTemp(InValue.Storage); - InValue.ResetImpl(); - } - else ResetImpl(); + return *reinterpret_cast(Storage.GetValuePtr()); } - constexpr void ResetImpl() { GetCallableImpl() = nullptr; } - }; NAMESPACE_PRIVATE_END @@ -276,22 +537,21 @@ public: TFunctionRef() = delete; TFunctionRef(const TFunctionRef& InValue) = default; - TFunctionRef(TFunctionRef&& InValue) = default; + TFunctionRef(TFunctionRef&& InValue) = default; + // We delete the assignment operators because we don't want it to be confused with being related to + // regular C++ reference assignment - i.e. calling the assignment operator of whatever the reference + // is bound to - because that's not what TFunctionRef does, nor is it even capable of doing that. TFunctionRef& operator=(const TFunctionRef& InValue) = delete; - TFunctionRef& operator=(TFunctionRef&& InValue) = delete; + TFunctionRef& operator=(TFunctionRef&& InValue) = delete; - template requires (!CTFunctionRef> && !CTInPlaceType> + template requires (!CTFunctionRef> && NAMESPACE_PRIVATE::TIsInvocableSignature>::Value) FORCEINLINE TFunctionRef(T&& InValue) { - using DecayedType = TDecay; checkf(NAMESPACE_PRIVATE::FunctionIsBound(InValue), TEXT("Cannot bind a null/unbound callable to a TFunctionRef")); - Impl::template EmplaceImpl(Forward(InValue)); + Impl::template Emplace(Forward(InValue)); } - - template - TFunctionRef(const T&&) = delete; }; @@ -300,79 +560,67 @@ class TFunction : public NAMESPACE_PRIVATE::TFunctionImpl< typename NAMESPACE_PRIVATE::TFunctionInfo::Fn, typename NAMESPACE_PRIVATE::TFunctionInfo::CVRef, - false> + false, false> { private: using Impl = NAMESPACE_PRIVATE::TFunctionImpl< typename NAMESPACE_PRIVATE::TFunctionInfo::Fn, typename NAMESPACE_PRIVATE::TFunctionInfo::CVRef, - false>; + false, false>; public: - constexpr TFunction(nullptr_t = nullptr) { Impl::ResetImpl(); } + constexpr TFunction(nullptr_t = nullptr) { Impl::Invalidate(); } - FORCEINLINE TFunction(const TFunction& InValue) = default; - FORCEINLINE TFunction(TFunction&& InValue) : Impl(MoveTemp(InValue)) { InValue.ResetImpl(); } - - FORCEINLINE TFunction& operator=(const TFunction& InValue) - { - Impl::AssignImpl(InValue); - return *this; - } - - FORCEINLINE TFunction& operator=(TFunction&& InValue) - { - if (&InValue == this) return *this; - Impl::AssignImpl(MoveTemp(InValue)); - return *this; - } + FORCEINLINE TFunction(const TFunction& InValue) = default; + FORCEINLINE TFunction(TFunction&& InValue) = default; + FORCEINLINE TFunction& operator=(const TFunction& InValue) = default; + FORCEINLINE TFunction& operator=(TFunction&& InValue) = default; template requires (!CTInPlaceType> && !CTFunctionRef> && !CTFunction> && !CTUniqueFunction> && CConstructibleFrom, T&&> && CCopyConstructible> + && CMoveConstructible> && CDestructible> && NAMESPACE_PRIVATE::TIsInvocableSignature>::Value) FORCEINLINE TFunction(T&& InValue) { - using DecayedType = TDecay; - if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Impl::ResetImpl(); - else Impl::template EmplaceImpl(Forward(InValue)); + if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Impl::Invalidate(); + else Impl::template Emplace(Forward(InValue)); } template requires (NAMESPACE_PRIVATE::TIsInvocableSignature>::Value - && CConstructibleFrom, ArgTypes...> && CCopyConstructible>) + && CConstructibleFrom, ArgTypes...> && CCopyConstructible> + && CMoveConstructible> && CDestructible>) FORCEINLINE TFunction(TInPlaceType, ArgTypes&&... Args) { - using DecayedType = TDecay; - Impl::template EmplaceImpl(Forward(Args)...); + Impl::template Emplace(Forward(Args)...); } - constexpr TFunction& operator=(nullptr_t) { Impl::ResetImpl(); return *this; } + constexpr TFunction& operator=(nullptr_t) { Reset(); return *this; } template requires (NAMESPACE_PRIVATE::TIsInvocableSignature>::Value && !CTFunctionRef> && !CTFunction> && !CTUniqueFunction> - && CConstructibleFrom, T&&> && CCopyConstructible>) + && CConstructibleFrom, T&&> && CCopyConstructible> + && CMoveConstructible> && CDestructible>) FORCEINLINE TFunction& operator=(T&& InValue) { - using DecayedType = TDecay; - - if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Impl::ResetImpl(); - else Impl::template EmplaceImpl(Forward(InValue)); + if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Reset(); + else Emplace(Forward(InValue)); return *this; } template requires (NAMESPACE_PRIVATE::TIsInvocableSignature>::Value - && CConstructibleFrom, ArgTypes...> && CCopyConstructible>) + && CConstructibleFrom, ArgTypes...> && CCopyConstructible> + && CMoveConstructible> && CDestructible>) FORCEINLINE TDecay& Emplace(ArgTypes&&... Args) { - using DecayedType = TDecay; - Impl::template EmplaceImpl(Forward(Args)...); - return Impl::template Target(); + Impl::Destroy(); + return Impl::template Emplace(Forward(Args)...); } - constexpr void Reset() { Impl::ResetImpl(); } + constexpr void Reset() { Impl::Destroy(); Impl::Invalidate(); } }; @@ -381,96 +629,86 @@ class TUniqueFunction : public NAMESPACE_PRIVATE::TFunctionImpl< typename NAMESPACE_PRIVATE::TFunctionInfo::Fn, typename NAMESPACE_PRIVATE::TFunctionInfo::CVRef, - false> + false, true> { private: using Impl = NAMESPACE_PRIVATE::TFunctionImpl< typename NAMESPACE_PRIVATE::TFunctionInfo::Fn, typename NAMESPACE_PRIVATE::TFunctionInfo::CVRef, - false>; + false, true>; public: - constexpr TUniqueFunction(nullptr_t = nullptr) { Impl::ResetImpl(); } - - FORCEINLINE TUniqueFunction(const TUniqueFunction& InValue) = delete; - TUniqueFunction(TUniqueFunction&& InValue) : Impl(MoveTemp(InValue)) { InValue.ResetImpl(); } + constexpr TUniqueFunction(nullptr_t = nullptr) { Impl::Invalidate(); } + FORCEINLINE TUniqueFunction(const TUniqueFunction& InValue) = delete; + FORCEINLINE TUniqueFunction(TUniqueFunction&& InValue) = default; FORCEINLINE TUniqueFunction& operator=(const TUniqueFunction& InValue) = delete; - FORCEINLINE TUniqueFunction& operator=(TUniqueFunction&& InValue) - { - if (&InValue == this) return *this; - Impl::AssignImpl(MoveTemp(InValue)); - return *this; - } + FORCEINLINE TUniqueFunction& operator=(TUniqueFunction&& InValue) = default; FORCEINLINE TUniqueFunction(const TFunction& InValue) - : Impl(*reinterpret_cast(&InValue)) - { } + { + new (this) TFunction(InValue); + } FORCEINLINE TUniqueFunction(TFunction&& InValue) - : Impl(MoveTemp(*reinterpret_cast(&InValue))) { - InValue.Reset(); + new (this) TFunction(MoveTemp(InValue)); } FORCEINLINE TUniqueFunction& operator=(const TFunction& InValue) { - Impl::AssignImpl(*reinterpret_cast(&InValue)); + *reinterpret_cast*>(this) = InValue; return *this; } FORCEINLINE TUniqueFunction& operator=(TFunction&& InValue) { - Impl::AssignImpl(MoveTemp(*reinterpret_cast(&InValue))); + *reinterpret_cast*>(this) = MoveTemp(InValue); return *this; } template requires (!CTInPlaceType> && !CTFunctionRef> && !CTFunction> && !CTUniqueFunction> - && CConstructibleFrom, T&&> && CMoveConstructible> + && CConstructibleFrom, T&&> && CMoveConstructible> && CDestructible> && NAMESPACE_PRIVATE::TIsInvocableSignature>::Value) FORCEINLINE TUniqueFunction(T&& InValue) { - using DecayedType = TDecay; - if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Impl::ResetImpl(); - else Impl::template EmplaceImpl(Forward(InValue)); + if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Impl::Invalidate(); + else Impl::template Emplace(Forward(InValue)); } template requires (NAMESPACE_PRIVATE::TIsInvocableSignature>::Value - && CConstructibleFrom, ArgTypes...> && CMoveConstructible>) + && CConstructibleFrom, ArgTypes...> && CMoveConstructible> && CDestructible>) FORCEINLINE TUniqueFunction(TInPlaceType, ArgTypes&&... Args) { - using DecayedType = TDecay; - Impl::template EmplaceImpl(Forward(Args)...); + Impl::template Emplace(Forward(Args)...); } - constexpr TUniqueFunction& operator=(nullptr_t) { Impl::ResetImpl(); return *this; } + constexpr TUniqueFunction& operator=(nullptr_t) { Impl::Destroy(); Impl::Invalidate(); return *this; } template requires (NAMESPACE_PRIVATE::TIsInvocableSignature>::Value && !CTFunctionRef> && !CTFunction> && !CTUniqueFunction> - && CConstructibleFrom, T&&> && CMoveConstructible>) + && CConstructibleFrom, T&&> && CMoveConstructible> && CDestructible>) FORCEINLINE TUniqueFunction& operator=(T&& InValue) { - using DecayedType = TDecay; - - if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Impl::ResetImpl(); - else Impl::template EmplaceImpl(Forward(InValue)); + if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Reset(); + else Emplace(Forward(InValue)); return *this; } template requires (NAMESPACE_PRIVATE::TIsInvocableSignature>::Value - && CConstructibleFrom, ArgTypes...> && CMoveConstructible>) + && CConstructibleFrom, ArgTypes...> && CMoveConstructible> && CDestructible>) FORCEINLINE TDecay& Emplace(ArgTypes&&... Args) { + Impl::Destroy(); using DecayedType = TDecay; - Impl::template EmplaceImpl(Forward(Args)...); - return Impl::template Target(); + return Impl::template Emplace(Forward(Args)...); } - constexpr void Reset() { Impl::ResetImpl(); } + constexpr void Reset() { Impl::Destroy(); Impl::Invalidate(); } }; @@ -495,6 +733,9 @@ constexpr bool operator==(const TUniqueFunction& LHS, nullptr_t) static_assert(sizeof(TFunction) == 64, "The byte size of TFunction is unexpected"); static_assert(sizeof(TUniqueFunction) == 64, "The byte size of TUniqueFunction is unexpected"); +static_assert(alignof(TFunction) == 16, "The byte alignment of TFunction is unexpected"); +static_assert(alignof(TUniqueFunction) == 16, "The byte alignment of TUniqueFunction is unexpected"); + NAMESPACE_PRIVATE_BEGIN template diff --git a/Redcraft.Utility/Source/Public/Templates/Optional.h b/Redcraft.Utility/Source/Public/Templates/Optional.h index 0cc76bd..db3368b 100644 --- a/Redcraft.Utility/Source/Public/Templates/Optional.h +++ b/Redcraft.Utility/Source/Public/Templates/Optional.h @@ -44,7 +44,7 @@ public: constexpr explicit TOptional(FInPlace, Ts&&... Args) : bIsValid(true) { - new(&Value) OptionalType(Forward(Args)...); + new (&Value) OptionalType(Forward(Args)...); } template requires (CConstructibleFrom) @@ -58,7 +58,7 @@ public: constexpr TOptional(const TOptional& InValue) requires (CCopyConstructible && !CTriviallyCopyConstructible) : bIsValid(InValue.IsValid()) { - if (InValue.IsValid()) new(&Value) OptionalType(InValue.GetValue()); + if (InValue.IsValid()) new (&Value) OptionalType(InValue.GetValue()); } constexpr TOptional(TOptional&& InValue) requires (CTriviallyMoveConstructible) = default; @@ -66,21 +66,21 @@ public: constexpr TOptional(TOptional&& InValue) requires (CMoveConstructible && !CTriviallyMoveConstructible) : bIsValid(InValue.IsValid()) { - if (InValue.IsValid()) new(&Value) OptionalType(MoveTemp(InValue.GetValue())); + if (InValue.IsValid()) new (&Value) OptionalType(MoveTemp(InValue.GetValue())); } template requires (CConstructibleFrom && TAllowUnwrapping::Value) constexpr explicit (!CConvertibleTo) TOptional(const TOptional& InValue) : bIsValid(InValue.IsValid()) { - if (InValue.IsValid()) new(&Value) OptionalType(InValue.GetValue()); + if (InValue.IsValid()) new (&Value) OptionalType(InValue.GetValue()); } template requires (CConstructibleFrom && TAllowUnwrapping::Value) constexpr explicit (!CConvertibleTo) TOptional(TOptional&& InValue) : bIsValid(InValue.IsValid()) { - if (InValue.IsValid()) new(&Value) OptionalType(MoveTemp(InValue.GetValue())); + if (InValue.IsValid()) new (&Value) OptionalType(MoveTemp(InValue.GetValue())); } constexpr ~TOptional() requires (CTriviallyDestructible) = default; @@ -106,7 +106,7 @@ public: if (IsValid()) GetValue() = InValue.GetValue(); else { - new(&Value) OptionalType(InValue.GetValue()); + new (&Value) OptionalType(InValue.GetValue()); bIsValid = true; } @@ -129,7 +129,7 @@ public: if (IsValid()) GetValue() = MoveTemp(InValue.GetValue()); else { - new(&Value) OptionalType(MoveTemp(InValue.GetValue())); + new (&Value) OptionalType(MoveTemp(InValue.GetValue())); bIsValid = true; } @@ -149,7 +149,7 @@ public: if (IsValid()) GetValue() = InValue.GetValue(); else { - new(&Value) OptionalType(InValue.GetValue()); + new (&Value) OptionalType(InValue.GetValue()); bIsValid = true; } @@ -169,7 +169,7 @@ public: if (IsValid()) GetValue() = MoveTemp(InValue.GetValue()); else { - new(&Value) OptionalType(MoveTemp(InValue.GetValue())); + new (&Value) OptionalType(MoveTemp(InValue.GetValue())); bIsValid = true; } @@ -182,7 +182,7 @@ public: if (IsValid()) GetValue() = Forward(InValue); else { - new(&Value) OptionalType(Forward(InValue)); + new (&Value) OptionalType(Forward(InValue)); bIsValid = true; } @@ -194,7 +194,7 @@ public: { Reset(); - OptionalType* Result = new(&Value) OptionalType(Forward(Args)...); + OptionalType* Result = new (&Value) OptionalType(Forward(Args)...); bIsValid = true; return *Result; diff --git a/Redcraft.Utility/Source/Public/Templates/Variant.h b/Redcraft.Utility/Source/Public/Templates/Variant.h index 5966c01..6d96ff5 100644 --- a/Redcraft.Utility/Source/Public/Templates/Variant.h +++ b/Redcraft.Utility/Source/Public/Templates/Variant.h @@ -110,7 +110,7 @@ public: : TypeIndex(I) { using SelectedType = TVariantAlternative>; - new(&Value) SelectedType(Forward(Args)...); + new (&Value) SelectedType(Forward(Args)...); } template requires (CConstructibleFrom) @@ -188,7 +188,7 @@ public: else { Reset(); - new(&Value) SelectedType(Forward(InValue)); + new (&Value) SelectedType(Forward(InValue)); TypeIndex = TVariantIndex>; } @@ -202,7 +202,7 @@ public: Reset(); using SelectedType = TVariantAlternative>; - SelectedType* Result = new(&Value) SelectedType(Forward(Args)...); + SelectedType* Result = new (&Value) SelectedType(Forward(Args)...); TypeIndex = I; return *Result;