I have an Actor of “ClassA” with delegate declaration, private delegate, and delegate accessor:
// ClassA: delegate declaration
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FStateChangedDelegate, FDelegateEvent, delegateEvent);
// ClassA: private instance of delegate
FStateChangedDelegate _stateChangeDelegates;
// ClassA: accessor for binding an event
FORCEINLINE FStateChangedDelegate& GetStateChangedDelegate()
{
return _stateChangeDelegates;
}
I also have an Actor of “Class B” that wants to bind and listen for events on the ClassA delegate. Right now it has to (1) get delegate and (2) bind delegate.
// ClassB: event callback that will be bound to the delegate
UFUNCTION()
void StateChangeEvent_1(FDelegateEvent delegateEvent);
// ClassB: doing the binding here, get the delegate from the class A object, add the new dynamic event
ClassA* object->GetStateChangedDelegate().AddDynamic(this, &ClassB::StateChangeEvent_1);
But, would it be cleaner to just pass the “ClassB::StateChangeEvent_1” function from ClassB to ClassA and not have to expose the private delegate?
How to do that with a new function in ClassA?
// ClassA: new function to bind an event function passed in
FORCEINLINE void AddStateChangedEvent(object, ???) ***// problem: what do the parameters look like here?***
{
_stateChangeDelegates.AddDynamic(object, ???);
}
// ClassB: new code in to bind the event
ClassA* object->AddStateChangedEvent(this, &ClassB::StateChangeEvent_1);
Any ideas on this? Or would this make the binding even uglier?
Getting close but not quite. This is in the ballpark but still not sure what parameter 1 should be.
// dynamic delegate declaration
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FStateChangedDelegate, FDelegateEvent&, delegateEvent);
// the dynamic delegate
FStateChangedDelegate _stateChangeDelegates;
/**
* Add new callback event to this object's state change delegate.
*/
FORCEINLINE void AddStateChangedEvent(**UObject* object**, TFunction<void(FDelegateEvent&)>& callback)
{
_stateChangeDelegates.AddDynamic(**object**, callback); **// compiler bombs on "object" what should it be?**
}
The third parameter(name) is generated by the preprocessor and thats why second param requires the form Class::Method.
You can try to wrap TFunction in a UObject, like:
class UWrapper : public UObject
{
GENERATED_BODY()
private:
TFunction<void(FDelegateEvent&)> CallFn;
public:
template<typename T>
void Bind(T* Instance, void(T::*Function)(FDelegateEvent&))
{
CallFn = &](FDelegateEvent& Ev) { (Instance->*Function)(Ev); };
}
void Dispatch(FDelegateEvent& Ev) { CallFn(Ev); }
};
// Then use like
template<typename T>
void ASomeActor::AddStateChangeEvent(T* Listener, void(T::*Function)(FDelegateEvent&))
{
auto Wrapper = NewObject<UWrapper>();
Wrapper->Bind(Listener, Function);
MyDelegate.AddDynamic(Wrapper, &UWrapper::Dispatch);
}
Written as-is, not tested, Just an idea.
Maybe there is another way, but I don’t know because I have never felt like diving into this delegates macro hell.