Function pointer to dynamic delegate?

I am trying to write a method which creates dynamic delegates from a function pointer argument, similar to this:

OnSomeDelegate.AddUObject(this, &UMyObject::ActOnSomeDelegate);

My function looks like this:

template <typename UserClass>
void ListenForRoutedInputAction(UserClass* InObject, typename TMemFunPtrType<false, UserClass, void()>::Type InFunc) {
	FOnSomeDelegate MyDelegate;
	MyDelegate.BindDynamic(InObject, InFunc);
	// DoStuff with it.
}

The problem is that BindDynamic is a macro, which does not evaulate InObject or InFunc runtime but simply passes “InObject” and “InFunc”:


Assertion failed: Result && Result[2] != (TCHAR)'0' [File:K:\Unreal Engine\UE_5.1\Engine\Source\Runtime\Core\Public\Delegates\Delegate.h] [Line: 474] 'InFunc' does not look like a member function

UnrealEditor_CorePlugin!UE::Delegates::Private::GetTrimmedMemberFunctionName() [K:\Unreal Engine\UE_5.1\Engine\Source\Runtime\Core\Public\Delegates\Delegate.h:474]

I believe what I am trying to do is uncommon, but I’d still like to find a way to do this, so I can easily set up my function pointers in one method call. I see the BindAction method on input component doing it but using a type I can’t access.

Related:

Bind delegate with function pointer
How do I bind a function pointer's function to a delegate?

BindAction in UInputComponent/UEnhancedInputComponent works because neither uses dynamic delegates. They use the regular non-dynamic delegates instead. UEnhancedInputComponent calls either CreateUObject or BindUFunction in the wrapper TEnhancedInputUnifiedDelegate (see https://github.com/EpicGames/UnrealEngine/blob/cdaec5b33ea5d332e51eee4e4866495c90442122/Engine/Plugins/EnhancedInput/Source/EnhancedInput/Public/EnhancedInputComponent.h#L80). BindUFunction would be the equivalent for the dynamic delegate which just takes any UObject* as well as the FName of the function to call. That’s why the bound function must also be a UFUNCTION.

For the dynamic delegate you could call __Internal_BindDynamic (which is what the BindDynamic macro internally does in the end). However __Internal_BindDynamic needs the function pointer as well as the name (and the UObject* of course). The macro basically guarantees that they match since the name is automatically derived from the argument of the macro (via UE::Delegates::Private::GetTrimmedMemberFunctionName). Without the macro that would no longer be guaranteed since you could basically pass in anything so that’s not really recommended.

NOTE:  Do not call this function directly.  Instead, call BindDynamic() which is a macro proxy function that automatically sets the function name string for the caller.
1 Like

Thanks for that :slight_smile: . The reason I use a dynamic delegate is because I want blueprint users to have a delegate parameter on a related method. The templated method of my example is what c++ users use to create such delegate. The actual logic is then processed in the UFUNCTION which takes a dynamic delegate as an argument.

Attempting to call BindDynamic by unwrapping the macros to functions led me to other errors, I can’t seem to figure out the syntax for a non macro solution. It is clear that the macro expects the name to be passed in, it doesn’t retrieve it dynamically.

  //Callback.__Internal_BindDynamic(InObject, InFunc, UE::Delegates::Private::GetTrimmedMemberFunctionName(TEXT(InFunc)));
  //Callback.__Internal_BindDynamic(InObject, InFunc, STATIC_FUNCTION_FNAME(InFunc));

BindUFunction Seems the way to go, since it’s not a macro and it should just work fine. The remaining challenge for both solutions seems to be getting the function name from the function pointer. Is this possible?

I keep running into macros which I can’t use to retrieve anything since they are macros :sweat_smile:

// Returns FName(TEXT("FunctionName")), while statically verifying that the function exists in ClassName
#define GET_FUNCTION_NAME_CHECKED(ClassName, FunctionName) \
	((void)sizeof(&ClassName::FunctionName), FName(TEXT(#FunctionName)))

If only I could either get the name from the function pointer or something like that

I’m not sure I understand what your purpose is, but you can have blueprint exposed delegates as function arguments relatively easy

Use DECLARE_DYNAMIC_DELEGATE to declare your delegate signature, and then in your BlueprintCallable UFUNCTION you can use it as an argument, which will show in blueprints as the red delegate pin
Then in your C++ code you can call ExecuteIfBound on the argument, and it will call the function that the user has connected in the blueprint. If you want to do something else with it you can always use .GetUObject and .GetFunctionName

Yes I did that already :slight_smile: now I am on the c++ side trying to bind a function pointer to a dynamic delegate.

1 Like

Why not just use the same delegate as the argument? Create a local variable of your delegate’s type, bind your UFUNCTION to it, and then pass it to the same blueprint exposed function

Because this is clean:

DoStuff(this, &USomeClas::SomeMethod);

And this is not:

FOnDelegate Delegate;
Delegate.Bind.....(this, &USomeClas::SomeMethod);
DoStuff(Delegate);

I also need the function pointers. c++ is enough bloat already in UE :slight_smile:

1 Like

This part confuses me, if you already have the dynamic delegate bound in blueprint and passed to your function why would you need to bind it again in C++? Or do you just want to change the binding of the given delegate?

Otherwise I would have suggested you use a non-dynamic delegate internally and either use .GetUObject and .GetFunctionName along BindUObject as @zeaf already meantiond or simply wrap/capture the dynamic delegate in a lambda and use BindLambda on the non-dynamic delegate.

The BindDynamic macro has one very important functionality and that’s converting the argument to a string using # in TEXT( #FuncName ). So if you pass the function pointer directly to the macro it passes the string "&UMyObject::ActOnSomeDelegate" to GetTrimmedMemberFunctionName. But if you wrap that in another function as an argument InFunc you lose that information and the string passed to GetTrimmedMemberFunctionName would just become "InFunc" which is obviously not what we want.

If you’re willing to change your parameter to UFunction * instead and use FindFunction/FindFunctionChecked wherever you call it you can get the name from the UFunction (but you already need to know the name to find it in the first place).

Another alternative would be to replace the function ListenForRoutedInputAction with a macro as well.

I have a method “ListenForRoutedInputAction” for c++ which takes a function pointer. I have an overloaded “ListenForRoutedInputAction” which takes a dynamic delegate. The reason for this is that I want blueprint users to implement functionality with a dynamic delegate, but I also want to do this in c++ without having to do this many times:

FOnDelegate Delegate;
Delegate.Bind.....(this, &USomeClas::SomeMethod);
DoStuff(Delegate);

That is why there is another method taking a function pointer.

Right now I am doing this, seems to be ok:

void UMyClass::DoStuff(const FName& InFunctionName) {
	FOnDelegate Delegate;
	check(GetClass()->FindFunctionByName(InFunctionName) != nullptr);
	Delegate.BindUFunction(this, InFunctionName);
	DoStuff(Delegate);
}

DoStuff(TEXT("SomeFunction"));
1 Like

You can’t do that without macro, which is why Epic uses a macro (AddDynamic) in front of their actual template. The macro extracts function name from pointer, and the template method just uses the function name. The function pointer is not really used it’s just for compilation type checks.

You can do the exact same thing. Add FuncName to your template, and add a macro in front of it to extract function name from function pointer :

template<typename UserClass>
void __Internal_ListenForRoutedInputAction(UserClass* InObject, typename TMemFunPtrType<false, UserClass, void()>::Type InFunc, const FName& FuncName)
{
    FOnSomeDelegate MyDelegate;
    MyDelegate.__Internal_BindDynamic(InObject, InFunc, FuncName);
    // DoStuff with it
}

#define ListenForRoutedInputAction(InObject, InFunc) __Internal_ListenForRoutedInputAction(InObject, InFunc, STATIC_FUNCTION_FNAME(TEXT(#InFunc)))
2 Likes

Thanks for the full code :slight_smile: I marked the earlier post as a solution which mentioned this as well, but this is the code I am using right now. While a macro, it is a better solution than what I did with “DoStuff(const FName& InFunctionName)”