Skip to content

Instantly share code, notes, and snippets.

@SammyJames
Last active May 20, 2019 18:46
Show Gist options
  • Save SammyJames/9ece78e3a11aec316194b806016744bd to your computer and use it in GitHub Desktop.
Save SammyJames/9ece78e3a11aec316194b806016744bd to your computer and use it in GitHub Desktop.
A wrapper to handle native and script delegates
#pragma once
#include "Delegate.h"
#include "Delegates/IDelegateInstance.h"
#include "LogMacros.h"
#include "UnrealTypeTraits.h"
namespace Private
{
template <bool, class T>
struct TMultiDelUnwrapBase
{
};
template <class T>
struct TMultiDelUnwrapBase<true, T>
{
template <class>
struct Unwrap;
template <class WeakType, class... Args>
struct Unwrap<TBaseDynamicDelegate<WeakType, void, Args...>*>
{
using Base = TMulticastDelegate<void, Args...>;
};
using BaseType = typename Unwrap<typename T::FDelegate*>::Base;
};
template <class T>
struct TMultiDelTypeUnwrap
: TMultiDelUnwrapBase<
TPointerIsConvertibleFromTo<T, TMulticastScriptDelegate<FWeakObjectPtr>>::Value,
T>
{
};
} // namespace Private
/**
* A unified delegate that takes a multicast script (dynamic) delegate and generates a native
* delegate from that type owned by this struct. Construct with a reference to the script delegate,
* and broadcast on this struct to invoke both native and script.
* @tparam ScriptType a multicast script (dynamic) delegate
*/
template <class ScriptType>
struct TUnifiedMulticastDelegate
{
using THelper = typename Private::TMultiDelTypeUnwrap<ScriptType>;
TUnifiedMulticastDelegate() = delete;
TUnifiedMulticastDelegate(const TUnifiedMulticastDelegate&) = delete;
#if !NO_LOGGING
~TUnifiedMulticastDelegate()
{
UE_CLOG(m_nNativeInvocations != m_nScriptInvocations,
LogTemp,
Warning,
TEXT("Native delegate invocation count is not equal to script delegate invocation "
"count: native = %d, script = %d"),
m_nNativeInvocations,
m_nScriptInvocations);
}
#else
~TUnifiedMulticastDelegate() = default;
#endif // !NO_LOGGING
TUnifiedMulticastDelegate(ScriptType& InScript)
: m_NativeDelegate()
, m_pScriptDelegate(&InScript)
{
}
TUnifiedMulticastDelegate(TUnifiedMulticastDelegate&& Other)
: m_NativeDelegate(MoveTemp(Other.m_NativeDelegate))
, m_pScriptDelegate(Other.m_pScriptDelegate)
{
m_pScriptDelegate = nullptr;
}
TUnifiedMulticastDelegate& operator=(TUnifiedMulticastDelegate&& Other)
{
m_NativeDelegate = MoveTemp(Other.m_NativeDelegate);
m_pScriptDelegate = Other.m_pScriptDelegate;
Other.m_pScriptDelegate = nullptr;
return *this;
}
template <class Object,
class... Args,
class = typename TEnableIf<TIsDerivedFrom<Object, UObject>::IsDerived, Object>::Type>
FDelegateHandle Add(Object* pObject, void (Object::*InMethod)(Args...))
{
return m_NativeDelegate.template AddUObject<Object>(pObject, InMethod);
}
template <class Object,
class... Args,
class = typename TEnableIf<TIsDerivedFrom<Object, UObject>::IsDerived, Object>::Type>
FDelegateHandle Add(Object* pObject, void (Object::*InMethod)(Args...) const)
{
return m_NativeDelegate.template AddUObject<Object>(pObject, InMethod);
}
template <class Object, class... Args>
FDelegateHandle Add(const TSharedPtr<Object>& pObject, void (Object::*InMethod)(Args...))
{
return m_NativeDelegate.template AddSP<Object>(pObject, InMethod);
}
template <class Object, class... Args>
FDelegateHandle Add(const TSharedPtr<Object>& pObject, void (Object::*InMethod)(Args...) const)
{
return m_NativeDelegate.template AddSP<Object>(pObject, InMethod);
}
template <class Object, class... Args>
FDelegateHandle
Add(typename TEnableIf<!TIsDerivedFrom<Object, UObject>::IsDerived, Object>::Type* pObject,
void (Object::*InMethod)(Args...))
{
return m_NativeDelegate.template AddRaw<Object>(pObject, InMethod);
}
template <class Object, class... Args>
FDelegateHandle
Add(typename TEnableIf<!TIsDerivedFrom<Object, UObject>::IsDerived, Object>::Type* pObject,
void (Object::*InMethod)(Args...) const)
{
return m_NativeDelegate.template AddRaw<Object>(pObject, InMethod);
}
template <class Functor>
FDelegateHandle Add(Functor&& InFunc)
{
return m_NativeDelegate.template AddLambda<Functor>(Forward<Functor>(InFunc));
}
template <class... Args>
FDelegateHandle Add(void (*InMethod)(Args...))
{
return m_NativeDelegate.template AddStatic(InMethod);
}
void Remove(FDelegateHandle&& InHandle)
{
if (InHandle.IsValid())
{
m_NativeDelegate.Remove(InHandle);
InHandle.Reset();
}
}
template <class Object>
void RemoveAll(Object* pObject)
{
m_NativeDelegate.RemoveAll(pObject);
}
template <class... Args>
void Broadcast(Args&&... InArgs) const
{
if (m_NativeDelegate.IsBound())
{
#if !NO_LOGGING
++m_nNativeInvocations;
UE_LOG(
LogTemp, Log, TEXT("Invoking native delegate, count = %d"), m_nNativeInvocations);
#endif // !NO_LOGGING
m_NativeDelegate.Broadcast(Forward<Args>(InArgs)...);
}
if (m_pScriptDelegate && m_pScriptDelegate->IsBound())
{
#if !NO_LOGGING
++m_nScriptInvocations;
UE_LOG(
LogTemp, Log, TEXT("Invoking script delegate, count = %d"), m_nScriptInvocations);
#endif // !NO_LOGGING
m_pScriptDelegate->Broadcast(Forward<Args>(InArgs)...);
}
}
private:
typename THelper::BaseType m_NativeDelegate;
ScriptType* m_pScriptDelegate = nullptr;
#if !NO_LOGGING
mutable uint32 m_nNativeInvocations = 0u;
mutable uint32 m_nScriptInvocations = 0u;
#endif // !NO_LOGGING
};
@SammyJames
Copy link
Author

SammyJames commented May 20, 2019

using it like so:

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FSomeDelegate);

UCLASS()
class UMyType : public UObject {
    GENERATED_BODY()

    using FUnifiedDel = TUnifiedMulticastDelegate<FSomeDelegate>;
public:
    UPROPERTY(BlueprintAssignable)
    FSomeDelegate MyDelegate;

    FUnifiedDel m_UnifiedDelegate = FUnifiedDel(MyDelegate);

    void SomethingHappened() {
        m_UnifiedDelegate.Broadcast(); // invokes both native and script delegates
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment