How to declare a generic delegate signature as a parameter Unreal/C++?

I’m trying to create a wrapper object that sets up enhanced action bindings by basically wrapping UEnhancedInputComponent::BindAction(), however I’m having a hard time declaring the wrapper method parameter so that BindAction() accepts it.

Basically I want to define my wrapper like so:

void InputWrapper::BindThrottleAction
(   UPlayerInputComponent* PlayerInputComponent,
    TSubclassOf<UObject> *Receiver,
    TFunction<void(const struct FInputActionValue& Value)> &UpdateThrottleSignatureAddr
) 
{
    // This fails to compile
    Cast<UEnhancedInputComponent>(PlayerInputComponent)->BindAction(ThrottleAction, ETriggerEvent::Triggered, Receiver, UpdateThrottleSignatureAddr);
}

And then the PlayerPawn calls the following in SetupPlayerInputComponent

void ASomePlayerPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{ 
//...
	InputWrapper->BindThrottleAction(PlayerInputComponent, this, &ASomePlayerPawn::UpdateThrottle);
}

I’m sure there’s a bunch wrong here, and I can’t seem to get the UpdateThrottleSignatureAddr parameter correctly defined so that I can pass it to EnhancedInputComponent->BindAction()

The only other ways I know how to do this are:

  1. Use the EnhancedInputComponent method that takes a function name FName FunctionName, and have PlayerPawn pass an FName. But my goal is to make this strongly typed, and I generally want to avoid passing function names as strings.
  2. Create a UInterface that the ASomePlayerPawn conforms to, and have InputWrapper::BindThrottleAction accept the interface. However my goal is a complete separation of concerns, so I’d prefer not to have the Interface a requirement.

Before I move onto option 2, I wanted to see if there was a more streamlined way, so that PlayerPawn doesn’t have to declare that it conforms to an interface, basically like how EnhancedInputComponent->BindAction works.

For reference here’s the generic typed EnhancedInputComponent method I’m trying to pass the signature to:

template<class UserClass>
FInputActionBinding & BindAction
(
    const FName ActionName,
    const EInputEvent KeyEvent,
    UserClass * Object,
    typename FInputActionHandlerWithKeySignature::TUObjectMethodDelegate< UserClass >::FMethodPtr Func
) 

Thanks!

Well, one problem is that TSubClassOf * is a pointer to a UClass pointer. That isn’t what you want. You want a UObject*.

Second, the function ptr could be defined as simply: FInputActionHandlerWithKeySignature instead of the whole TFunction<void… thing.
To use that, you would call it with FInputActionHandlerWithKeySignature::CreateUObject(this, &SomeClass::SomeCallback).

Third, it looks like the bind functions you are using are not supposed to be used. Instead use the ones defined by DEFINE_BIND_ACTION:

DECLARE_DELEGATE(FEnhancedInputActionHandlerSignature);
DECLARE_DELEGATE_OneParam(FEnhancedInputActionHandlerValueSignature, const FInputActionValue&);
DECLARE_DELEGATE_OneParam(FEnhancedInputActionHandlerInstanceSignature, const FInputActionInstance&);	// Provides full access to value and timers

    DEFINE_BIND_ACTION(FEnhancedInputActionHandlerSignature);
	DEFINE_BIND_ACTION(FEnhancedInputActionHandlerValueSignature);
	DEFINE_BIND_ACTION(FEnhancedInputActionHandlerInstanceSignature);

Each DEFINE_BIND_ACTION unwraps into a function with this signature:

#define DEFINE_BIND_ACTION(HANDLER_SIG)
template<class UserClass, typename... VarTypes>
FEnhancedInputActionEventBinding& BindAction(const UInputAction* Action, ETriggerEvent TriggerEvent, UserClass* Object, typename HANDLER_SIG::template TMethodPtr< UserClass, VarTypes... > Func, VarTypes... Vars)

So, choose 1 of the 3 signatures in the DEFINE_BIND_ACTIONs, and use that defined delegate as your parameter, called with the aforementioned Delegate::CreateUObject (or other Create function you feel like using).

Let me know if that works/you have any questions. I have not tried compiling the code myself so I could be wrong.

1 Like

Your comments helped and I got it to compile by making it templated method:

	template<class UserClass>
	inline void BindThrottleAction(UInputComponent* PlayerInputComponent, UserClass* Reciever, FEnhancedInputActionHandlerValueSignature::TMethodPtr<UserClass> Signature)
	{
		UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent);

		if (!ensure(!(EnhancedInputComponent == nullptr))) return;
		if (!ensure(!ThrottleAction)) return;

		EnhancedInputComponent->BindAction(ThrottleAction, ETriggerEvent::Triggered, Reciever, Signature);
		EnhancedInputComponent->BindAction(ThrottleAction, ETriggerEvent::Completed, Reciever, Signature);
	}

And the caller calls it like so:

InputWrapper->BindThrottleAction(PlayerInputComponent, this, &SomePlayerPawn::UpdateThrottle);

But I’m not getting the SomePlayerPawn::UpdateThrottle callback to execute. I think the issue has something to do with referencing the class properties in the template method. I think it’s failing those ensure calls, (or it’s just hanging).~

I then tried to have the template call a private method that then performed the actual work, declared as follows:

void PrivateBindThrottleAction(UInputComponent* PlayerInputComponent, void* Reciever, FEnhancedInputActionHandlerValueSignature* Signature)

And passing using your recommendedFInputActionHandlerWithKeySignature::CreateUObject(). That works perfectly for the signature, BUT the compiler couldn’t resolve passing void * to BindAction().

I then thought of getting a UClass object of the UserClass object and casting the void * using the UClass object. But that doesn’t seem supported.

At this point I’m going to just do something more simple. I think this is too in the C++ weeds for me.

Thanks for the help though! I’d be interested if there were a way to do this.

EDIT: I was wrong. The template was working perfectly fine! I had this ensure backward: if (!ensure(!ThrottleAction)) return; :person_facepalming:

Thanks for the help!