#pragma once #include "CoreTypes.h" #include "Memory/Memory.h" #include "Concepts/Same.h" #include "Templates/Any.h" #include "Templates/Tuple.h" #include "Templates/Invoke.h" #include "Templates/Utility.h" #include "Templates/TypeHash.h" #include "Templates/Container.h" #include "Concepts/Comparable.h" #include "Concepts/Convertible.h" #include "TypeTraits/TypeTraits.h" #include "Miscellaneous/TypeInfo.h" #include "Concepts/BooleanTestable.h" #include "Miscellaneous/AssertionMacros.h" NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) NAMESPACE_PRIVATE_BEGIN enum class EFunctionType { Reference, Object, Unique, }; enum class EFunctionSpecifiers { None, LValue, RValue, Const, ConstLValue, ConstRValue, }; template struct TFunctionImpl; template struct TIsTFunctionImpl : FFalse { }; template struct TIsTFunctionImpl> : FTrue { }; template struct TIsTFunctionRef : FFalse { }; template struct TIsTFunctionRef> : FTrue { }; template struct TIsTFunction : FFalse { }; template struct TIsTFunction> : FTrue { }; template struct TIsTUniqueFunction : FFalse { }; template struct TIsTUniqueFunction> : FTrue { }; struct FFunctionIsBound { template static constexpr bool F(const T& Func) { if constexpr (TIsPointer::Value || TIsMemberPointer::Value || TIsTFunctionImpl::Value) { return !!Func; } else { return true; } } }; template struct TIsInvocableResultWithSpecifiers : FFalse { }; template struct TIsInvocableResultWithSpecifiers : TBoolConstant::Value && TIsInvocableResult::Value> { }; template struct TIsInvocableResultWithSpecifiers : TIsInvocableResult { }; template struct TIsInvocableResultWithSpecifiers : TIsInvocableResult { }; template struct TIsInvocableResultWithSpecifiers : TBoolConstant::Value && TIsInvocableResult::Value> { }; template struct TIsInvocableResultWithSpecifiers : TIsInvocableResult { }; template struct TIsInvocableResultWithSpecifiers : TIsInvocableResult { }; template struct TFunctionCallSpecifiers; template struct TFunctionCallSpecifiers { using Type = T& ; }; template struct TFunctionCallSpecifiers { using Type = T& ; }; template struct TFunctionCallSpecifiers { using Type = T&&; }; template struct TFunctionCallSpecifiers { using Type = const T& ; }; template struct TFunctionCallSpecifiers { using Type = const T& ; }; template struct TFunctionCallSpecifiers { using Type = const T&&; }; template struct TFunctionImpl { public: using ResultType = R; using ArgumentType = TTuple; constexpr TFunctionImpl(nullptr_t = nullptr) requires (FunctionType != EFunctionType::Reference) : Callable(nullptr) { } TFunctionImpl(const TFunctionImpl& InValue) requires (FunctionType != EFunctionType::Unique) : Callable(InValue.Callable), Storage(InValue.Storage) { } TFunctionImpl(TFunctionImpl&& InValue) : Callable(InValue.Callable), Storage(MoveTemp(InValue.Storage)) { if constexpr (FunctionType != EFunctionType::Reference) InValue.Reset(); } template requires (!TIsTFunctionImpl::Type>::Value) && (!TIsTInPlaceType::Type>::Value) && TIsInvocableResultWithSpecifiers::Type, Types...>::Value && (FunctionType == EFunctionType::Reference || TIsConstructible::Type, T&&>::Value) && ((FunctionType == EFunctionType::Object && TIsCopyConstructible::Type>::Value) || (FunctionType == EFunctionType::Unique && TIsMoveConstructible::Type>::Value) || FunctionType == EFunctionType::Reference) FORCEINLINE TFunctionImpl(T&& InValue) { using DecayedFunctorType = typename TDecay::Type; if constexpr (FunctionType == EFunctionType::Reference) { checkf(FFunctionIsBound::F(InValue), TEXT("Cannot bind a null/unbound callable to a TFunctionRef")); } if (!FFunctionIsBound::F(InValue)) Callable = nullptr; else EmplaceImpl(Forward(InValue)); } template requires (FunctionType != EFunctionType::Reference) && TIsInvocableResultWithSpecifiers::Type, Types...>::Value && TIsConstructible::Type, ArgTypes...>::Value && ((FunctionType == EFunctionType::Object && TIsCopyConstructible::Type>::Value) || (FunctionType == EFunctionType::Unique && TIsMoveConstructible::Type>::Value)) FORCEINLINE TFunctionImpl(TInPlaceType, ArgTypes&&... Args) { using DecayedFunctorType = typename TDecay::Type; EmplaceImpl(Forward(Args)...); } template requires (FunctionType == EFunctionType::Reference) && (OtherFunctionType != EFunctionType::Reference) FORCEINLINE TFunctionImpl(const TFunctionImpl& InValue) { checkf(FFunctionIsBound::F(InValue), TEXT("Cannot bind a null/unbound callable to a TFunctionRef")); EmplaceImpl>(InValue); } FORCEINLINE TFunctionImpl(const TFunctionImpl& InValue) requires (FunctionType == EFunctionType::Unique) : Callable((*reinterpret_cast(&InValue)).Callable), Storage((*reinterpret_cast(&InValue)).Storage) { } FORCEINLINE TFunctionImpl(TFunctionImpl&& InValue) requires (FunctionType == EFunctionType::Unique) : Callable((*reinterpret_cast(&InValue)).Callable), Storage(MoveTemp((*reinterpret_cast(&InValue)).Storage)) { InValue.Reset(); } ~TFunctionImpl() = default; FORCEINLINE TFunctionImpl& operator=(const TFunctionImpl& InValue) requires (FunctionType == EFunctionType::Object) { AssignImpl(InValue); return *this; } FORCEINLINE TFunctionImpl& operator=(TFunctionImpl&& InValue) requires (FunctionType != EFunctionType::Reference) { if (&InValue == this) return *this; AssignImpl(MoveTemp(InValue)); return *this; } FORCEINLINE TFunctionImpl& operator=(const TFunctionImpl& InValue) requires (FunctionType == EFunctionType::Unique) { AssignImpl(*reinterpret_cast(&InValue)); return *this; } FORCEINLINE TFunctionImpl& operator=(TFunctionImpl&& InValue) requires (FunctionType == EFunctionType::Unique) { AssignImpl(MoveTemp(*reinterpret_cast(&InValue))); return *this; } constexpr TFunctionImpl& operator=(nullptr_t) requires (FunctionType != EFunctionType::Reference) { Reset(); return *this; } template requires (FunctionType != EFunctionType::Reference) && (!TIsTFunctionImpl::Type>::Value) && TIsInvocableResultWithSpecifiers::Type, Types...>::Value && TIsConstructible::Type, T&&>::Value && ((FunctionType == EFunctionType::Object && TIsCopyConstructible::Type>::Value) || (FunctionType == EFunctionType::Unique && TIsMoveConstructible::Type>::Value)) FORCEINLINE TFunctionImpl& operator=(T&& InValue) { using DecayedFunctorType = typename TDecay::Type; if (!FFunctionIsBound::F(InValue)) Reset(); else EmplaceImpl(Forward(InValue)); return *this; } template requires (FunctionType != EFunctionType::Reference) && TIsInvocableResultWithSpecifiers::Type, Types...>::Value && TIsConstructible::Type, ArgTypes...>::Value && ((FunctionType == EFunctionType::Object && TIsCopyConstructible::Type>::Value) || (FunctionType == EFunctionType::Unique && TIsMoveConstructible::Type>::Value)) FORCEINLINE typename TDecay::Type& Emplace(ArgTypes&&... Args) { using DecayedFunctorType = typename TDecay::Type; EmplaceImpl(Forward(Args)...); return Target(); } FORCEINLINE ResultType operator()(Types... Args) requires (Specifiers == EFunctionSpecifiers::None ) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Types... Args) & requires (Specifiers == EFunctionSpecifiers::LValue ) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Types... Args) && requires (Specifiers == EFunctionSpecifiers::RValue ) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Types... Args) const requires (Specifiers == EFunctionSpecifiers::Const ) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Types... Args) const& requires (Specifiers == EFunctionSpecifiers::ConstLValue) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Types... Args) const&& requires (Specifiers == EFunctionSpecifiers::ConstRValue) { return CallImpl(Forward(Args)...); } constexpr bool IsValid() const { return Callable != nullptr; } constexpr explicit operator bool() const { return Callable != nullptr; } FORCEINLINE const FTypeInfo& TargetType() const requires (FunctionType != EFunctionType::Reference) { return IsValid() ? Storage.GetTypeInfo() : Typeid(void); }; template FORCEINLINE T& Target() & requires (FunctionType != EFunctionType::Reference) && TIsSame::Type>::Value && TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value { return static_cast< StorageType& >(Storage).template GetValue(); } template FORCEINLINE T&& Target() && requires (FunctionType != EFunctionType::Reference) && 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 (FunctionType != EFunctionType::Reference) && 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 (FunctionType != EFunctionType::Reference) && TIsSame::Type>::Value && TIsObject::Type>::Value && (!TIsArray::Type>::Value) && TIsDestructible::Type>::Value { return static_cast(Storage).template GetValue(); } constexpr void Reset() requires (FunctionType != EFunctionType::Reference) { Callable = nullptr; } constexpr void Swap(TFunctionImpl& InValue) requires (FunctionType != EFunctionType::Reference) { if (!IsValid() && !InValue.IsValid()) return; if (IsValid() && !InValue.IsValid()) { InValue = MoveTemp(*this); Reset(); return; } if (InValue.IsValid() && !IsValid()) { *this = MoveTemp(InValue); InValue.Reset(); return; } NAMESPACE_REDCRAFT::Swap(Callable, InValue.Callable); NAMESPACE_REDCRAFT::Swap(Storage, InValue.Storage); } private: using StorageType = typename TConditional>::Type; using StorageRef = typename TConditional::Type&>::Type; using CallFunc = ResultType(*)(StorageRef, Types&&...); StorageType Storage; CallFunc Callable; template FORCEINLINE void EmplaceImpl(ArgTypes&&... Args) { if constexpr (FunctionType == EFunctionType::Reference) Storage = ((void*)&Args, ...); else Storage.template Emplace(Forward(Args)...); Callable = [](StorageRef Storage, Types&&... Args) -> ResultType { const auto GetFunc = [&Storage]() -> decltype(auto) { if constexpr (FunctionType == EFunctionType::Reference) return *reinterpret_cast(Storage); else return Storage.template GetValue(); }; return InvokeResult(Forward::Type>(GetFunc()), Forward(Args)...); }; } 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)...); } FORCEINLINE void AssignImpl(const TFunctionImpl& InValue) { if (InValue.IsValid()) { Callable = InValue.Callable; Storage = InValue.Storage; } else Reset(); } FORCEINLINE void AssignImpl(TFunctionImpl&& InValue) { if (InValue.IsValid()) { Callable = InValue.Callable; Storage = MoveTemp(InValue.Storage); InValue.Reset(); } else Reset(); } }; template struct TFunctionSelect; template struct TFunctionSelect { using Type = TFunctionImpl; }; template struct TFunctionSelect { using Type = TFunctionImpl; }; template struct TFunctionSelect { using Type = TFunctionImpl; }; template struct TFunctionSelect { using Type = TFunctionImpl; }; template struct TFunctionSelect { using Type = TFunctionImpl; }; template struct TFunctionSelect { using Type = TFunctionImpl; }; NAMESPACE_PRIVATE_END inline constexpr size_t FUNCTION_DEFAULT_INLINE_SIZE = 32; inline constexpr size_t FUNCTION_DEFAULT_INLINE_ALIGNMENT = 16; template using TFunctionRef = typename NAMESPACE_PRIVATE::TFunctionSelect::Type; template using TFunction = typename NAMESPACE_PRIVATE::TFunctionSelect::Type; template using TUniqueFunction = typename NAMESPACE_PRIVATE::TFunctionSelect::Type; template struct TIsTFunctionRef : NAMESPACE_PRIVATE::TIsTFunctionRef { }; template struct TIsTFunction : NAMESPACE_PRIVATE::TIsTFunction { }; template struct TIsTUniqueFunction : NAMESPACE_PRIVATE::TIsTUniqueFunction { }; 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 NotFunctionType { F Func; NotFunctionType(const NotFunctionType&) = default; NotFunctionType(NotFunctionType&&) = default; template constexpr NotFunctionType(InF&& InFunc) : Func(Forward(InFunc)) { } template requires TIsInvocable::Value constexpr auto operator()(Types&&... Args) & -> decltype(!Invoke(Func, Forward(Args)...)) { return !Invoke(Func, Forward(Args)...); } template requires TIsInvocable::Value constexpr auto operator()(Types&&... Args) && -> decltype(!Invoke(MoveTemp(Func), Forward(Args)...)) { return !Invoke(MoveTemp(Func), Forward(Args)...); } template requires TIsInvocable::Value constexpr auto operator()(Types&&... Args) const& -> decltype(!Invoke(Func, Forward(Args)...)) { return !Invoke(Func, Forward(Args)...); } template requires TIsInvocable::Value constexpr auto operator()(Types&&... Args) const&& -> decltype(!Invoke(MoveTemp(Func), Forward(Args)...)) { return !Invoke(MoveTemp(Func), Forward(Args)...); } }; NAMESPACE_PRIVATE_END template constexpr NAMESPACE_PRIVATE::NotFunctionType::Type> NotFn(F&& Func) { return NAMESPACE_PRIVATE::NotFunctionType::Type>(Forward(Func)); } NAMESPACE_MODULE_END(Utility) NAMESPACE_MODULE_END(Redcraft) NAMESPACE_REDCRAFT_END