#pragma once #include "CoreTypes.h" #include "Templates/Any.h" #include "Templates/Tuple.h" #include "Templates/Invoke.h" #include "Memory/Alignment.h" #include "Templates/Utility.h" #include "TypeTraits/TypeTraits.h" #include "Miscellaneous/AssertionMacros.h" // NOTE: Disable alignment limit warning #pragma warning(disable : 4359) NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) inline constexpr size_t FUNCTION_DEFAULT_INLINE_SIZE = ANY_DEFAULT_INLINE_SIZE - sizeof(uintptr); inline constexpr size_t FUNCTION_DEFAULT_INLINE_ALIGNMENT = ANY_DEFAULT_INLINE_ALIGNMENT; template requires TIsFunction::Value struct TFunctionRef; template requires TIsFunction::Value && (Memory::IsValidAlignment(InlineAlignment)) struct TFunction; template requires TIsFunction::Value && (Memory::IsValidAlignment(InlineAlignment)) struct TUniqueFunction; template struct TIsTFunctionRef : FFalse { }; template struct TIsTFunctionRef> : FTrue { }; template struct TIsTFunction : FFalse { }; template struct TIsTFunction> : FTrue { }; template struct TIsTUniqueFunction : FFalse { }; template struct TIsTUniqueFunction> : FTrue { }; NAMESPACE_PRIVATE_BEGIN template constexpr bool FunctionIsBound(const T& Func) { if constexpr (TIsPointer::Value || TIsMemberPointer::Value || TIsTFunctionRef::Value || TIsTFunction::Value || TIsTUniqueFunction::Value) { return !!Func; } else { return true; } } template struct TIsInvocableSignature : FFalse { }; template struct TIsInvocableSignature : TBoolConstant::Value && TIsInvocableResult::Value> { }; template struct TIsInvocableSignature : TIsInvocableResult { }; template struct TIsInvocableSignature : TIsInvocableResult { }; template struct TIsInvocableSignature : TBoolConstant::Value && TIsInvocableResult::Value> { }; template struct TIsInvocableSignature : TIsInvocableResult { }; template struct TIsInvocableSignature : TIsInvocableResult { }; template struct TFunctionInfo; template struct TFunctionInfo { using Fn = Ret(Types...); using CVRef = int; }; template struct TFunctionInfo { using Fn = Ret(Types...); using CVRef = int&; }; template struct TFunctionInfo { using Fn = Ret(Types...); using CVRef = int&&; }; template struct TFunctionInfo { using Fn = Ret(Types...); using CVRef = const int; }; template struct TFunctionInfo { using Fn = Ret(Types...); using CVRef = const int&; }; template struct TFunctionInfo { using Fn = Ret(Types...); using CVRef = const int&&; }; template struct TFunctionImpl; template struct alignas(InlineAlignment) TFunctionImpl { public: using ResultType = Ret; using ArgumentType = TTuple; TFunctionImpl() = default; TFunctionImpl(const TFunctionImpl&) = default; TFunctionImpl(TFunctionImpl&& InValue) = default; TFunctionImpl& operator=(const TFunctionImpl&) = default; TFunctionImpl& operator=(TFunctionImpl&&) = default; ~TFunctionImpl() = default; FORCEINLINE ResultType operator()(Types... Args) requires (TIsSame::Value) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Types... Args) & requires (TIsSame::Value) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Types... Args) && requires (TIsSame::Value) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Types... Args) const requires (TIsSame::Value) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Types... Args) const& requires (TIsSame::Value) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Types... Args) const&& requires (TIsSame::Value) { return CallImpl(Forward(Args)...); } constexpr bool IsValid() const { return Callable != nullptr; } constexpr explicit operator bool() const { return Callable != nullptr; } FORCEINLINE const type_info& TargetType() const requires (!bIsRef) { return IsValid() ? Storage.GetTypeInfo() : typeid(void); }; template FORCEINLINE T& Target() & requires (!bIsRef) && TIsSame::Type>::Value && TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value { return static_cast< StorageType& >(Storage).template GetValue(); } template FORCEINLINE T&& Target() && requires (!bIsRef) && TIsSame::Type>::Value && TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value { return static_cast< StorageType&&>(Storage).template GetValue(); } template FORCEINLINE const T& Target() const& requires (!bIsRef) && TIsSame::Type>::Value && TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value { return static_cast(Storage).template GetValue(); } template FORCEINLINE const T&& Target() const&& requires (!bIsRef) && TIsSame::Type>::Value && TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value { 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(Callable, InValue.Callable); Swap(Storage, InValue.Storage); } private: using StorageType = typename TConditional::Type*, TAny>::Type; using StorageRef = typename TConditional::Type*, typename TCopyCVRef::Type&>::Type; using CallFunc = ResultType(*)(StorageRef, Types&&...); StorageType Storage; CallFunc Callable; FORCEINLINE ResultType CallImpl(Types&&... Args) { checkf(IsValid(), TEXT("Attempting to call an unbound TFunction!")); return Callable(Storage, Forward(Args)...); } FORCEINLINE ResultType CallImpl(Types&&... Args) const { checkf(IsValid(), TEXT("Attempting to call an unbound TFunction!")); return Callable(Storage, Forward(Args)...); } protected: template FORCEINLINE void EmplaceImpl(ArgTypes&&... Args) { using CallableType = typename TCopyConst::Type, DecayedType>::Type; if constexpr (bIsRef) Storage = ((reinterpret_cast(AddressOf(Args))), ...); else Storage.template Emplace(Forward(Args)...); Callable = [](StorageRef Storage, Types&&... Args) -> ResultType { using InvokeType = typename TConditional< TIsReference::Value, typename TCopyCVRef::Type, typename TCopyCVRef::Type& >::Type; const auto GetFunc = [&Storage]() -> InvokeType { if constexpr (!bIsRef) return Storage.template GetValue(); else return static_cast(*reinterpret_cast(Storage)); }; return InvokeResult(GetFunc(), Forward(Args)...); }; } FORCEINLINE void AssignImpl(const TFunctionImpl& InValue) { if (InValue.IsValid()) { Callable = InValue.Callable; Storage = InValue.Storage; } else ResetImpl(); } FORCEINLINE void AssignImpl(TFunctionImpl&& InValue) { if (InValue.IsValid()) { Callable = InValue.Callable; Storage = MoveTemp(InValue.Storage); InValue.ResetImpl(); } else ResetImpl(); } constexpr void ResetImpl() { Callable = nullptr; } }; NAMESPACE_PRIVATE_END template requires TIsFunction::Value struct TFunctionRef : public NAMESPACE_PRIVATE::TFunctionImpl< typename NAMESPACE_PRIVATE::TFunctionInfo::Fn, typename NAMESPACE_PRIVATE::TFunctionInfo::CVRef, FUNCTION_DEFAULT_INLINE_SIZE, FUNCTION_DEFAULT_INLINE_ALIGNMENT, true> { private: using Super = NAMESPACE_PRIVATE::TFunctionImpl< typename NAMESPACE_PRIVATE::TFunctionInfo::Fn, typename NAMESPACE_PRIVATE::TFunctionInfo::CVRef, FUNCTION_DEFAULT_INLINE_SIZE, FUNCTION_DEFAULT_INLINE_ALIGNMENT, true>; public: TFunctionRef() = delete; TFunctionRef(const TFunctionRef& InValue) = default; TFunctionRef(TFunctionRef&& InValue) = default; TFunctionRef& operator=(const TFunctionRef& InValue) = delete; TFunctionRef& operator=(TFunctionRef&& InValue) = delete; template requires (!TIsTFunctionRef::Type>::Value) && (!TIsTInPlaceType::Type>::Value) && NAMESPACE_PRIVATE::TIsInvocableSignature::Type>::Value FORCEINLINE TFunctionRef(T&& InValue) { using DecayedType = typename TDecay::Type; checkf(NAMESPACE_PRIVATE::FunctionIsBound(InValue), TEXT("Cannot bind a null/unbound callable to a TFunctionRef")); Super::template EmplaceImpl(Forward(InValue)); } template TFunctionRef(const T&&) = delete; }; template requires TIsFunction::Value && (Memory::IsValidAlignment(InlineAlignment)) struct TFunction : public NAMESPACE_PRIVATE::TFunctionImpl< typename NAMESPACE_PRIVATE::TFunctionInfo::Fn, typename NAMESPACE_PRIVATE::TFunctionInfo::CVRef, InlineSize, InlineAlignment, false> { private: using Super = NAMESPACE_PRIVATE::TFunctionImpl< typename NAMESPACE_PRIVATE::TFunctionInfo::Fn, typename NAMESPACE_PRIVATE::TFunctionInfo::CVRef, InlineSize, InlineAlignment, false>; public: constexpr TFunction(nullptr_t = nullptr) { Super::ResetImpl(); } FORCEINLINE TFunction(const TFunction& InValue) = default; FORCEINLINE TFunction(TFunction&& InValue) : Super(MoveTemp(InValue)) { InValue.ResetImpl(); } FORCEINLINE TFunction& operator=(const TFunction& InValue) { Super::AssignImpl(InValue); return *this; } FORCEINLINE TFunction& operator=(TFunction&& InValue) { if (&InValue == this) return *this; Super::AssignImpl(MoveTemp(InValue)); return *this; } template requires (!TIsTInPlaceType::Type>::Value) && (!TIsTFunctionRef::Type>::Value) && (!TIsTFunction::Type>::Value) && (!TIsTUniqueFunction::Type>::Value) && TIsConstructible::Type, T&&>::Value && TIsCopyConstructible::Type>::Value && NAMESPACE_PRIVATE::TIsInvocableSignature::Type>::Value FORCEINLINE TFunction(T&& InValue) { using DecayedType = typename TDecay::Type; if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Super::ResetImpl(); else Super::template EmplaceImpl(Forward(InValue)); } template requires NAMESPACE_PRIVATE::TIsInvocableSignature::Type>::Value && TIsConstructible::Type, ArgTypes...>::Value && TIsCopyConstructible::Type>::Value FORCEINLINE TFunction(TInPlaceType, ArgTypes&&... Args) { using DecayedType = typename TDecay::Type; Super::template EmplaceImpl(Forward(Args)...); } constexpr TFunction& operator=(nullptr_t) { Super::ResetImpl(); return *this; } template requires NAMESPACE_PRIVATE::TIsInvocableSignature::Type>::Value && (!TIsTFunctionRef::Type>::Value) && (!TIsTFunction::Type>::Value) && (!TIsTUniqueFunction::Type>::Value) && TIsConstructible::Type, T&&>::Value && TIsCopyConstructible::Type>::Value FORCEINLINE TFunction& operator=(T&& InValue) { using DecayedType = typename TDecay::Type; if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Super::ResetImpl(); else Super::template EmplaceImpl(Forward(InValue)); return *this; } template requires NAMESPACE_PRIVATE::TIsInvocableSignature::Type>::Value && TIsConstructible::Type, ArgTypes...>::Value&& TIsCopyConstructible::Type>::Value FORCEINLINE typename TDecay::Type& Emplace(ArgTypes&&... Args) { using DecayedType = typename TDecay::Type; Super::template EmplaceImpl(Forward(Args)...); return Super::template Target(); } constexpr void Reset() { Super::ResetImpl(); } }; template requires TIsFunction::Value && (Memory::IsValidAlignment(InlineAlignment)) struct TUniqueFunction : public NAMESPACE_PRIVATE::TFunctionImpl< typename NAMESPACE_PRIVATE::TFunctionInfo::Fn, typename NAMESPACE_PRIVATE::TFunctionInfo::CVRef, InlineSize, InlineAlignment, false> { private: using Super = NAMESPACE_PRIVATE::TFunctionImpl< typename NAMESPACE_PRIVATE::TFunctionInfo::Fn, typename NAMESPACE_PRIVATE::TFunctionInfo::CVRef, InlineSize, InlineAlignment, false>; public: constexpr TUniqueFunction(nullptr_t = nullptr) { Super::ResetImpl(); } FORCEINLINE TUniqueFunction(const TUniqueFunction& InValue) = delete; TUniqueFunction(TUniqueFunction&& InValue) : Super(MoveTemp(InValue)) { InValue.ResetImpl(); } FORCEINLINE TUniqueFunction& operator=(const TUniqueFunction& InValue) = delete; FORCEINLINE TUniqueFunction& operator=(TUniqueFunction&& InValue) { if (&InValue == this) return *this; Super::AssignImpl(MoveTemp(InValue)); return *this; } FORCEINLINE TUniqueFunction(const TFunction& InValue) : Super(*reinterpret_cast(&InValue)) { } FORCEINLINE TUniqueFunction(TFunction&& InValue) : Super(MoveTemp(*reinterpret_cast(&InValue))) { InValue.Reset(); } FORCEINLINE TUniqueFunction& operator=(const TFunction& InValue) { Super::AssignImpl(*reinterpret_cast(&InValue)); return *this; } FORCEINLINE TUniqueFunction& operator=(TFunction&& InValue) { Super::AssignImpl(MoveTemp(*reinterpret_cast(&InValue))); return *this; } template requires (!TIsTInPlaceType::Type>::Value) && (!TIsTFunctionRef::Type>::Value) && (!TIsTFunction::Type>::Value) && (!TIsTUniqueFunction::Type>::Value) && TIsConstructible::Type, T&&>::Value && TIsMoveConstructible::Type>::Value && NAMESPACE_PRIVATE::TIsInvocableSignature::Type>::Value FORCEINLINE TUniqueFunction(T&& InValue) { using DecayedType = typename TDecay::Type; if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Super::ResetImpl(); else Super::template EmplaceImpl(Forward(InValue)); } template requires NAMESPACE_PRIVATE::TIsInvocableSignature::Type>::Value && TIsConstructible::Type, ArgTypes...>::Value && TIsMoveConstructible::Type>::Value FORCEINLINE TUniqueFunction(TInPlaceType, ArgTypes&&... Args) { using DecayedType = typename TDecay::Type; Super::template EmplaceImpl(Forward(Args)...); } constexpr TUniqueFunction& operator=(nullptr_t) { Super::ResetImpl(); return *this; } template requires NAMESPACE_PRIVATE::TIsInvocableSignature::Type>::Value && (!TIsTFunctionRef::Type>::Value) && (!TIsTFunction::Type>::Value) && (!TIsTUniqueFunction::Type>::Value) && TIsConstructible::Type, T&&>::Value&& TIsMoveConstructible::Type>::Value FORCEINLINE TUniqueFunction& operator=(T&& InValue) { using DecayedType = typename TDecay::Type; if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Super::ResetImpl(); else Super::template EmplaceImpl(Forward(InValue)); return *this; } template requires NAMESPACE_PRIVATE::TIsInvocableSignature::Type>::Value && TIsConstructible::Type, ArgTypes...>::Value&& TIsMoveConstructible::Type>::Value FORCEINLINE typename TDecay::Type& Emplace(ArgTypes&&... Args) { using DecayedType = typename TDecay::Type; Super::template EmplaceImpl(Forward(Args)...); return Super::template Target(); } constexpr void Reset() { Super::ResetImpl(); } }; template constexpr bool operator==(const TFunctionRef& LHS, nullptr_t) { return !LHS; } template constexpr bool operator==(const TFunction& LHS, nullptr_t) { return !LHS; } template constexpr bool operator==(const TUniqueFunction& LHS, nullptr_t) { return !LHS; } static_assert(sizeof(TFunction) == 64, "The byte size of TFunction is unexpected"); static_assert(sizeof(TUniqueFunction) == 64, "The byte size of TUniqueFunction is unexpected"); NAMESPACE_PRIVATE_BEGIN template struct TNotFunction { F Storage; TNotFunction(const TNotFunction&) = default; TNotFunction(TNotFunction&&) = default; template constexpr TNotFunction(InF&& InFunc) : Storage(Forward(InFunc)) { } template requires TIsInvocable::Value constexpr auto operator()(Types&&... Args) & -> decltype(!Invoke(Storage, Forward(Args)...)) { return !Invoke(Storage, Forward(Args)...); } template requires TIsInvocable::Value constexpr auto operator()(Types&&... Args) && -> decltype(!Invoke(MoveTemp(Storage), Forward(Args)...)) { return !Invoke(MoveTemp(Storage), Forward(Args)...); } template requires TIsInvocable::Value constexpr auto operator()(Types&&... Args) const& -> decltype(!Invoke(Storage, Forward(Args)...)) { return !Invoke(Storage, Forward(Args)...); } template requires TIsInvocable::Value constexpr auto operator()(Types&&... Args) const&& -> decltype(!Invoke(MoveTemp(Storage), Forward(Args)...)) { return !Invoke(MoveTemp(Storage), Forward(Args)...); } }; NAMESPACE_PRIVATE_END template constexpr NAMESPACE_PRIVATE::TNotFunction::Type> NotFn(F&& Func) { return NAMESPACE_PRIVATE::TNotFunction::Type>(Forward(Func)); } NAMESPACE_MODULE_END(Utility) NAMESPACE_MODULE_END(Redcraft) NAMESPACE_REDCRAFT_END