Pass function as parameter to set up delegates

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? :frowning:

2 Likes

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?**
}


AddDynamic is a macro, the preprocessor expands it into a form;
From:



SomeDelegate.AddDynamic(OtherActor, &AOtherActor::SomeMethod)

To:


SomeDelegate.__Internal_AddDynamic(OtherActor, &AOtherActor::SomeMethod, "SomeMethod");


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.

3 Likes

Hmm. If it requires passing a struct that makes the code uglier. Gonna pass on this I guess. :frowning:

@Emaer thanks for the help though. :cool:

I needed that functionality just to beautify the code.
not to put million of checks for every button I have in UI.

My solution does not exactly fit your requirement. But maybe it could help somebody else…

So I decided to go with a simple macro like that:

#define BIND_UI_BUTTON(ButtonName, Callback)
if (!ensure(ButtonName))
{ return; }
if (!ButtonName->OnClicked.IsBound())
{ ButtonName->OnClicked.AddDynamic(this, Callback); }

2 Likes