14#include "CoreMinimal.h"
101 template <
typename Function, FEventPolicy DefaultPolicy = {}>
109 using MutexLock = std::conditional_t<DefaultPolicy.ThreadSafe, FScopeLock,
FVoid>;
112 using FDelegate = TDelegate<FunctionSignature, FDefaultDelegateUserPolicy>;
115 DefaultPolicy.CacheViaCopy,
116 TTuple<std::decay_t<Args>...>,
120 template <
typename... BroadcastArgs>
121 requires CConvertibleTo<TTuple<BroadcastArgs...>, TTuple<Args...>>
125 bHasBroadcasted =
true;
126 if constexpr (DefaultPolicy.CacheViaCopy)
136 MulticastDelegate.Broadcast(
FWD(args)...);
138 for (
const FDelegateHandle& handle : OnlyNextDelegates)
139 MulticastDelegate.Remove(handle);
141 OnlyNextDelegates.Empty();
154 template <
typename... OptionalObject>
requires (
sizeof...(OptionalObject) <= 1)
173 MutexLock lock(&Mutex.Get());
174 return AddInternal(delegate, policy);
181 template <CSameAs<FDelegate>... Delegates>
185 (AddInternal(delegates, {}), ...);
192 template <CSameAs<FDelegate>... Delegates>
195 (AddInternal(delegates, {}), ...);
209 template <CDynamicDelegate DynamicDelegateType>
210 FDelegateHandle
Add(
const DynamicDelegateType& dynamicDelegate,
FEventPolicy const& policy = {})
212 MutexLock lock(&Mutex.Get());
213 return AddInternal(
AsNative(dynamicDelegate), policy, {}, dynamicDelegate.GetUObject(), dynamicDelegate.GetFunctionName());
229 template <CDynamicDelegate DynamicDelegateType>
232 MutexLock lock(&Mutex.Get());
233 return AddUniqueInternal(
AsNative(dynamicDelegate), policy, dynamicDelegate.GetUObject(), dynamicDelegate.GetFunctionName());
237 bool RemoveInternal(
const FDelegateHandle& delegateHandle)
239 const bool result = MulticastDelegate.Remove(delegateHandle);
241 if (
const FBoundUFunction* key = BoundUFunctionsMap.FindKey(delegateHandle))
242 BoundUFunctionsMap.Remove(*key);
244 OnlyNextDelegates.Remove(delegateHandle);
257 bool Remove(
const FDelegateHandle& delegateHandle)
260 return RemoveInternal(delegateHandle);
270 template <
class DynamicDelegateType>
271 bool Remove(
const DynamicDelegateType& dynamicDelegate)
274 FDelegateHandle delegateHandle;
275 if (BoundUFunctionsMap.RemoveAndCopyValue(FBoundUFunction(dynamicDelegate.GetUObject(), dynamicDelegate.GetFunctionName()), delegateHandle))
276 return RemoveInternal(delegateHandle);
291 for (
auto it = BoundUFunctionsMap.CreateIterator(); it; ++it)
292 if (!it.Key().Key.IsValid() || it.Key().Key.Get() == inUserObject)
295 return MulticastDelegate.RemoveAll(inUserObject);
302 MulticastDelegate.Clear();
303 OnlyNextDelegates.Reset();
304 BoundUFunctionsMap.Reset();
305 bHasBroadcasted =
false;
312 return bHasBroadcasted;
317 FDelegateHandle AddUniqueInternal(
320 const UObject* boundObject,
321 const FName& boundFunctionName
323 FDelegateHandle uniqueHandle;
325 if (
const FDelegateHandle* delegateHandle = BoundUFunctionsMap.Find(FBoundUFunction(boundObject, boundFunctionName)))
326 uniqueHandle = *delegateHandle;
328 return AddInternal(delegate, policy, uniqueHandle, boundObject, boundFunctionName);
331 FDelegateHandle AddInternal(
333 FEventPolicy
const& policy,
334 FDelegateHandle
const& uniqueHandle = {},
335 const UObject* boundObject =
nullptr,
336 FName
const& boundFunctionName = NAME_None
338 const FEventPolicy actualPolicy = policy.With(DefaultPolicy);
340 if (bHasBroadcasted && actualPolicy.Belated && actualPolicy.Once)
342 CallBelated(delegate);
343 return FDelegateHandle();
346 FDelegateHandle outputHandle = uniqueHandle;
347 if (!outputHandle.IsValid())
349 outputHandle = MulticastDelegate.Add(delegate);
351 if (boundObject && boundFunctionName != NAME_None)
352 BoundUFunctionsMap.Add(FBoundUFunction(boundObject, boundFunctionName), outputHandle);
354 if (actualPolicy.Once)
355 OnlyNextDelegates.Add(outputHandle);
358 if (bHasBroadcasted && actualPolicy.Belated)
359 CallBelated(delegate);
364 void CallBelated(FDelegate& delegate)
369 using FBoundUFunction = TPair<TWeakObjectPtr<const UObject>, FName>;
371 bool bHasBroadcasted =
false;
374 TSet<FDelegateHandle> OnlyNextDelegates;
375 TMap<FBoundUFunction, FDelegateHandle> BoundUFunctionsMap;
376 TOptional<ArgumentsCache> Cache;
377 TMulticastDelegate<void(Args...), FDefaultDelegateUserPolicy> MulticastDelegate;
381 template <
typename Signature, FEventPolicy DefaultPolicy = {}>
385 template <
typename Signature, FEventPolicy DefaultPolicy = {}>
389 template <
typename Signature, FEventPolicy DefaultPolicy = {}>
393 template <
typename Signature, FEventPolicy DefaultPolicy = {}>
397 template <
typename Signature, FEventPolicy DefaultPolicy = {}>
401 template <
typename Signature, FEventPolicy DefaultPolicy = {}>
405 template <
typename Signature, FEventPolicy DefaultPolicy = {}>
407 DefaultPolicy.With({.Once =
true, .Belated =
true, .CacheViaCopy =
true})
411 template <CDynamicMulticastDelegate Dynamic, FEventPolicy DefaultPolicy = {}>
418 template <
typename Dynamic,
FEventPolicy DefaultPolicy = {}>
#define FWD(...)
Shorten forwarding expression with this macro so one may not need to specify explicit type.
FDelegateHandle Add(FDelegate delegate, FEventPolicy const &policy={})
Adds a new delegate to the event delegate.
int32 RemoveAll(const void *inUserObject)
Removes all binding associated to the given object.
std::conditional_t< DefaultPolicy.ThreadSafe, FScopeLock, FVoid > MutexLock
void(Args...) FunctionSignature
TEventDelegate & With(Delegates &&... delegates)
Bind multiple delegates at once, useful for initial mandatory bindings
TDelegate< FunctionSignature, FDefaultDelegateUserPolicy > FDelegate
FDelegate Delegation(OptionalObject &&... object)
Create a delegate object which is broadcasting this event. This is useful for chaining events togethe...
FDelegateHandle AddUnique(const DynamicDelegateType &dynamicDelegate, FEventPolicy const &policy={})
Adds the given dynamic delegate only if it's not already bound to this event delegate.
std::conditional_t< DefaultPolicy.CacheViaCopy, TTuple< std::decay_t< Args >... >, TTuple< Args... > > ArgumentsCache
void Reset()
Resets all states of this event delegate to their default.
FDelegateHandle Add(const DynamicDelegateType &dynamicDelegate, FEventPolicy const &policy={})
Adds a new dynamic delegate to this event delegate.
bool Remove(const FDelegateHandle &delegateHandle)
Remove the binding associated to the given handle.
void Broadcast(BroadcastArgs &&... args)
bool IsBroadcasted() const
bool Remove(const DynamicDelegateType &dynamicDelegate)
Remove the binding associated to the dynamic delegate.
TEventDelegate(Delegates... delegates)
Bind multiple delegates at once, useful for initial mandatory bindings.
"Extension" of a common TMulticastDelegate. It allows to define optional "flags" when adding a bindin...
TInferredDelegate< Function, Captures... > From(Function func, Captures &&... captures)
Instead of specifying manually a delegate type, infer it from the input function and the extra captur...
typename TNativeEvent_Struct< Dynamic, DefaultPolicy >::Type TNativeEvent
Map the input dynamic multicast delegate to a conceptually compatible native event delegate type.
NativeDelegateType AsNative(Dynamic &&dynamicDelegate)
Creates a native delegate that is bound to the same UFunction as the specified dynamic delegate.
TFunction_Return< Function > InvokeWithTuple(Function &&function, Tuple &&arguments)
A clone of std::apply for Unreal, STL and RangeV3 tuples which also supports function pointers.
This struct may be used for situations where something needs to be returned but it's not meaningful t...
Settings for the TEventDelegate class, which defines optional behavior when adding a binding to it.
bool ThreadSafe
Enable mutex locks around adding/broadcasting delegates. Only considered in DefaultPolicy.
bool Belated
The binding will be executed immediately if the delegate has already been broadcasted.
FORCEINLINE friend constexpr bool operator!=(FEventPolicy const &lhs, FEventPolicy const &rhs)
FORCEINLINE constexpr bool IsDefault() const
Is this instance equivalent to a default constructed one.
bool Once
The binding will be automatically removed after the next broadcast.
bool CacheViaCopy
Attempt to copy arguments when storing them for belated invokes, instead of perfect forwarding them....
FORCEINLINE friend constexpr bool operator==(FEventPolicy const &lhs, FEventPolicy const &rhs)
FORCEINLINE constexpr FEventPolicy With(FEventPolicy const &other) const
Merge two policy flags.
Map the input dynamic multicast delegate to a conceptually compatible native event delegate type.
A type wrapper around a default initializeable object which may not be copyable but which needs to be...