Is it possible to pass a function as another function's argument?

I’m working on abstracting switches and other interactions by creating a base Interaction component, whose only job is to let parent actors subscribe to its UseSubscribers event, and let outside functions call the function which broadcasts UseSubscribers. For purposes of cleanliness, I don’t want external functions directly modifying the event, so I’m trying to write a function to do the subscribing for me:

//This lives in the .h above everything
DECLARE_EVENT_OneParam(OwningType, FUseDelegate, AActor*)
FUseDelegate UseSubscribers;


//This lives in the .cpp
//This should ideally take a function as a second argument, and subscribe that function to UseSubscribers

void UInteraction::SubscribeUser(AActor* user){
	UseSubscribers.Add(/*Function ref goes here*/);
}

This must be absurdly simple, but I’m having a lot of trouble figuring out the syntax behind it- is there a simple and recommended way to pass the function’s address in memory as an argument, or should I just manually run something like ComponentRef->UseSubscribers.Add(&MyClass::MyFunction) from the actor doing the subscribing?

This looks like strange delegate decleration it should be DECLARE_DYNAMIC_DELEGATE. Now, there 2 types of delegates, singlecast and multicast, singlecast takes only one function (subscriber), multicast takes more then one and you decler it using DECLARE_DYNAMIC_MULTICAST_DELEGATE. Also you should decler them outside of the class, so it cereates structs not related to class. So it should looks like this:

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FUseDelegate, AActor*, ArgumentName)

class UInteraction : public UObject {
    FUseDelegate UseSubscribers;

}

You can also add UPROPERTY(BlueprintAssignable) before varable so you can use delegate in blueprints as event dispacher. As for binding, your way of doing looks really wirerd to, you don’t need to add function for that, object that wants to subscribe it can do that from outside of class:

GetInteractions()->UseSubscribers.AddDynamic(this,&ASomeActor::SomeFunction);

and for functions outside of UObject tree you use AddRaw insted of AddDynamic. All function used in AddDynamic need to be visible by engine in reflection system, so place UFUNCTION() before there declarations (can be empty, main function of UFUNCTION is to add function in to reflection system, making function blueprint callable is just bonus feature). If you use singlecast delegate you use word “Bind” instead of “Add”. To raise the event in delegate you call this:

UseSubscribers.Broadcast(SomeActorArgument,AddMoreArgumentsIfYouDecleredSo);

Yes, you can do this. You must first realize that compatibility among the delegate types is determined by their arguments. Now, declare these in your .h. You may declare them within your UInteraction class, or globally outside of the class. Either is valid.

DECLARE_DELEGATE_OneParam(FUseDelegate, AActor*);
DECLARE_EVENT_OneParam(UInteraction, FUseEvent, AActor*);

Now, add these method signature declarations to your UInteraction declaration in the .h:

public:
	FDelegateHandle SubscribeUser(FUseDelegate& UseDelegate);
	void UnsubscribeUser(FDelegateHandle DelegateHandle);
private:
	FUseEvent MyUseEvent;

And these implementations to your .cpp:

FDelegateHandle UInteraction::SubscribeUser(FUseDelegate& UseDelegate)
{
	return MyUseEvent.Add(UseDelegate);
}

void UInteraction::UnsubscribeUser(FDelegateHandle DelegateHandle)
{
	MyUseEvent.Remove(DelegateHandle);
}

And then, if another class wants to subscribe:

FUseDelegate Subscriber;
Subscriber.BindUObject(this, &ASomeActor::SomeListeningFunction);
FDelegateHandle Handle = SomeInteraction->SubscribeUser(Subscriber);

And if you wanted to unsubscribe, you would save the returned FDelegateHandle somewhere, and use it again later when calling UnsubscribeUser.

How does this work? You must understand two things:

  1. Delegates are first-class values that can be passed around as function arguments. (However, you should pass them by reference whenever possible, because creating a copy requires a heap allocation).

  2. Unicast delegates can be added into multi-cast delegates if they have compatible argument and return types.

It looks like the function signature of ::BeginSpeaking doesn’t match what your FUseDelegate is declared for. Can you show post the type declarations?

This is incredibly helpful, thank you! I’m just having a small problem with assigning the delegate we’re passing to the event when the subscriber is a subclass of AActor. I’m trying to copy your example by subscribing the BeginSpeaking(AActor* speaker) function on every ACustomCharacter to their interaction component:

FUseDelegate Subscriber;
Subscriber.BindUObject(this, &ACustomCharacter ::BeginSpeaking);
FDelegateHandle Handle = characterInteract->SubscribeUser(Subscriber);

This produces an error:

'void TBaseDelegate<TTypeWrapper<void>,AActor *>::BindUObject<ACustomCharacter,>(UserClass *,void (__cdecl ACustomCharacter::* )(AActor *) const)' : cannot convert argument 2 from 'void (__cdecl ACustomCharacter::* )(void)' to 'void (__cdecl ACustomCharacter::* )(AActor *)'
Info         with
Info         [
Info             UserClass=ACustomCharacter
Info         ]
Info         Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

I’m assuming this means that it has a problem accepting a child of AActor (my ACharacter subclass) and wants a cast, but casting ACustomCharacter as AActor doesn’t change the error.

Oh that was silly of me, you’re exactly right- fixing the dec made everything work, thank you for the super-detailed walkthrough! This answered a lot of questions that popped up looking over the docs :slight_smile: