x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

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?

Product Version: UE 4.8
Tags:
more ▼

asked Aug 21 '15 at 02:37 PM in C++ Programming

avatar image

HInoue
276 97 67 84

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

2 answers: sort voted first

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.

more ▼

answered Aug 21 '15 at 09:05 PM

avatar image

cancel
2.4k 101 72 124

avatar image HInoue Aug 22 '15 at 02:09 AM

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.

avatar image cancel Aug 22 '15 at 02:52 AM

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

avatar image HInoue Aug 22 '15 at 03:23 AM

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 :)

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

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);
more ▼

answered Aug 21 '15 at 07:08 PM

avatar image

Shadowriver
37.8k 937 172 1123

(comments are locked)
10|2000 characters needed characters left
Viewable by all users
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question