// Copyright 2020 Phyronnaz #pragma once #include "CoreMinimal.h" #include "VoxelSharedPtr.h" #include "VoxelEngineVersionHelpers.h" /** * Delegate impl for weak lambda on shared pointers */ template class TBaseSPFunctorDelegateInstance; template class TBaseSPFunctorDelegateInstance : public TCommonDelegateInstanceState { private: static_assert(TAreTypesEqual::Type>::Value, "FunctorType cannot be a reference"); using Super = TCommonDelegateInstanceState; using RetValType = typename Super::RetValType; using UnwrappedThisType = TBaseSPFunctorDelegateInstance; public: TBaseSPFunctorDelegateInstance(const TSharedPtr& InUserObject, const FunctorType& InFunctor) : Super () , UserObject(InUserObject) , Functor (InFunctor) { } TBaseSPFunctorDelegateInstance(const TSharedPtr& InUserObject, FunctorType&& InFunctor) : Super () , UserObject(InUserObject) , Functor (MoveTemp(InFunctor)) { } // IDelegateInstance interface #if USE_DELEGATE_TRYGETBOUNDFUNCTIONNAME FName TryGetBoundFunctionName() const final { return NAME_None; } #endif UObject* GetUObject() const final { return nullptr; } const void* GetObjectForTimerManager() const final { return UserObject.Pin().Get(); } uint64 GetBoundProgramCounterForTimerManager() const final { return 0; } // Deprecated bool HasSameObject(const void* InUserObject) const final { return UserObject.HasSameObject(InUserObject); } bool IsSafeToExecute() const final { return UserObject.IsValid(); } public: // IBaseDelegateInstance interface void CreateCopy(FDelegateBase& Base) final { new (Base) UnwrappedThisType(*(UnwrappedThisType*)this); } virtual RetValType Execute(ParamTypes... Params) const override final { typedef typename TRemoveConst::Type MutableUserClass; // Verify that the user object is still valid. We only have a weak reference to it. auto SharedUserObject = UserObject.Pin(); check(SharedUserObject.IsValid()); return Functor(Forward(Params)...); } virtual bool ExecuteIfSafe(ParamTypes... Params) const override final { // Verify that the user object is still valid. We only have a weak reference to it. if (TSharedPtr SharedUserObject = this->UserObject.Pin()) { (void)Functor(Forward(Params)...); return true; } return false; } public: /** * Creates a new shared pointer delegate binding for the given user object and lambda. * * @param InUserObjectRef Shared reference to the user's object that contains the class method. * @param InFunc Lambda * @return The new delegate. */ FORCEINLINE static void Create(FDelegateBase& Base, const TSharedPtr& InUserObjectRef, FunctorType&& InFunc) { new (Base) UnwrappedThisType(InUserObjectRef, MoveTemp(InFunc)); } /** * Creates a new shared pointer delegate binding for the given user object and lambda. * * This overload requires that the supplied object derives from TSharedFromThis. * * @param InUserObject The user's object that contains the class method. Must derive from TSharedFromThis. * @param InFunc Lambda * @return The new delegate. */ FORCEINLINE static void Create(FDelegateBase& Base, UserClass* InUserObject, FunctorType&& InFunc) { // We expect the incoming InUserObject to derived from TSharedFromThis. auto UserObjectRef = StaticCastSharedRef(InUserObject->AsShared()); Create(Base, UserObjectRef, MoveTemp(InFunc)); } private: // Context object - the validity of this object controls the validity of the lambda TWeakPtr UserObject; // C++ functor // We make this mutable to allow mutable lambdas to be bound and executed. We don't really want to // model the Functor as being a direct subobject of the delegate (which would maintain transivity of // const - because the binding doesn't affect the substitutability of a copied delegate. mutable typename TRemoveConst::Type Functor; }; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// template struct TDelegateFromLambda : TDelegateFromLambda { }; template struct TDelegateFromLambda : TDelegateFromLambda { }; template struct TDelegateFromLambda { using Type = TDelegate; template using TDelegateImpl = TBaseSPFunctorDelegateInstance; }; template inline auto MakeLambdaDelegate(TLambda Lambda) { return TDelegateFromLambda::Type::CreateLambda(MoveTemp(Lambda)); } template inline auto MakeWeakObjectPtrDelegate(T* Ptr, TLambda Lambda) { return TDelegateFromLambda::Type::CreateWeakLambda(const_cast::Type*>(Ptr), MoveTemp(Lambda)); } template inline auto MakeWeakPtrDelegate(const TSharedRef& Object, TLambda Lambda) { typename TDelegateFromLambda::Type Delegate; TDelegateFromLambda::template TDelegateImpl::Create(Delegate, Object, MoveTemp(Lambda)); return Delegate; } template inline auto MakeWeakPtrDelegate(TClass* Object, TLambda Lambda) { typename TDelegateFromLambda::Type Delegate; TDelegateFromLambda::template TDelegateImpl::Create(Delegate, Object, MoveTemp(Lambda)); return Delegate; } template inline auto MakeVoxelWeakPtrDelegate(TClass* Object, TLambda Lambda) { typename TDelegateFromLambda::Type Delegate; TDelegateFromLambda::template TDelegateImpl::Create(Delegate, Object, MoveTemp(Lambda)); return Delegate; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// template struct TLambdaConditionalForward : TLambdaConditionalForward { }; template struct TLambdaConditionalForward : TLambdaConditionalForward { }; template struct TLambdaConditionalForward { template static auto Create(TLambda Lambda, TGetCondition GetCondition, TCheckCondition CheckCondition) { return [=](TArgs... Args) { // Could be a shared pointer, or a bool auto&& Condition = GetCondition(); if (CheckCondition(Condition)) { Lambda(Forward(Args)...); } }; } }; template inline auto MakeWeakPtrLambda(const T& Ptr, TLambda Lambda) { return TLambdaConditionalForward::Create(Lambda, [WeakPtr = MakeWeakPtr(Ptr)]() { return WeakPtr.Pin(); }, [](const auto& InPtr) { return InPtr.IsValid(); }); } template inline auto MakeVoxelWeakPtrLambda(const T& Ptr, TLambda Lambda) { return TLambdaConditionalForward::Create(Lambda, [WeakPtr = MakeVoxelWeakPtr(Ptr)]() { return WeakPtr.Pin(); }, [](const auto& InPtr) { return InPtr.IsValid(); }); }