[C++] Pass a class member function to another function?

I have a class set up with a delegate that other classes can bind to like this.

Publisher->MyDelegate.BindUObject(this, &ASubscriber::MyDelegateFunction);

This all works fine without a problem. However, what I would like to do is pass the delegate function directly to the publisher class and let it decide how to bind and do any other operations it wants to do. Something like this for example.

Publisher->PerformBind(this, &ASubscriber::MyDelegateFunction);

Then internally, the publisher could do the binding like this.

MyDelegate.BindUObject(PassedInClass, PassedInFunction);

So I’m just wondering how I go about passing the ASubscriber class instance and its delegate function to my publisher class.

You can do the following:

Header:

DECLARE_DELEGATE_RetVal_OneParam(float, FMyDelegate, int);

UCLASS()
class USubscriber : public UObject
{
	GENERATED_BODY()

public:
	USubscriber();

	virtual float MyDelegateFunction(int Time);
	virtual float MyOtherDelegateFunction(int Time);

	float TimeMultiplier;
};

UCLASS()
class USlowSubscriber : public USubscriber
{
	GENERATED_BODY()

public:
	float MyDelegateFunction(int Time) override;
};

UCLASS()
class UPublisher : public UObject
{
	GENERATED_BODY()

public:
	FMyDelegate MyDelegate;
	
	void BindUObject(USubscriber* Subscriber, float (USubscriber::* GetTime)(int Time));
	void BindUClass(TSubclassOf<USubscriber> Class, float (USubscriber::* GetTime)(int Time));
};

Implementation:

USubscriber::USubscriber()
	: TimeMultiplier{ 10.f }
{
}

float USubscriber::MyDelegateFunction(int Time)
{
	return TimeMultiplier * Time;
}

float USubscriber::MyOtherDelegateFunction(int Time)
{
	return Time / TimeMultiplier;
}

float USlowSubscriber::MyDelegateFunction(int Time)
{
	UE_LOG(LogTemp, Warning, TEXT("I'm really slow..."));
	return TimeMultiplier * 100.f * Time;
}

void UPublisher::BindUObject(USubscriber* Subscriber, float (USubscriber::* GetTime)(int Time))
{
	if (Subscriber)
	{
		MyDelegate.BindUObject(Subscriber, GetTime);
	}
	if (MyDelegate.IsBound())
	{
		UE_LOG(LogTemp, Warning, TEXT("The value of 'MyDelegate.Execute(2)' is: %f"), MyDelegate.Execute(2));
	}

}

void UPublisher::BindUClass(TSubclassOf<USubscriber> Class, float (USubscriber::* GetTime)(int Time))
{
	USubscriber* Subscriber{ NewObject<USubscriber>(this, Class) };
	if (ensure(Subscriber))
	{
		MyDelegate.BindUObject(Subscriber, GetTime);
	}
	if (ensure(MyDelegate.IsBound()))
	{
		UE_LOG(LogTemp, Warning, TEXT("The value of 'MyDelegate.Execute(2)' is: %f"), MyDelegate.Execute(2));
	}
}

And then…

UPublisher* Publisher{ NewObject<UPublisher>(this) };

USubscriber* Subscriber{ NewObject<USubscriber>(this) };
Subscriber->TimeMultiplier = 10.f;

USubscriber* AnotherSubscriber{ NewObject<USubscriber>(this) };
AnotherSubscriber->TimeMultiplier = 2.f;

Publisher->BindUObject(Subscriber, &USubscriber::MyDelegateFunction);
Publisher->BindUObject(Subscriber, &USubscriber::MyOtherDelegateFunction);
Publisher->BindUObject(AnotherSubscriber, &USubscriber::MyDelegateFunction);
Publisher->BindUObject(AnotherSubscriber, &USubscriber::MyOtherDelegateFunction);

Publisher->BindUClass(USubscriber::StaticClass(), &USubscriber::MyDelegateFunction);
Publisher->BindUClass(USlowSubscriber::StaticClass(), &USubscriber::MyDelegateFunction);

will output

LogTemp:Warning: The value of 'MyDelegate.Execute(2)' is: 20.000000
LogTemp:Warning: The value of 'MyDelegate.Execute(2)' is: 0.200000
LogTemp:Warning: The value of 'MyDelegate.Execute(2)' is: 4.000000
LogTemp:Warning: The value of 'MyDelegate.Execute(2)' is: 1.000000
LogTemp:Warning: The value of 'MyDelegate.Execute(2)' is: 20.000000
LogTemp:Warning: I'm really slow...
LogTemp:Warning: The value of 'MyDelegate.Execute(2)' is: 2000.000000

But that means the publisher has to know about the subscriber right as you have USubscriber in your UObjectBind function declaration. I’m trying to find a solution that avoids that. The publisher is actually a UActorComponent, so it can be attached to any AActor in the scene, so it has no idea what the actor’s class type will be.

In BeginPlay, an actor can attach a publisher component to itself and bind to the publisher’s delegate. The problem is that this puts the responsibility on to the actor to know how to bind itself to the publisher. I’m hoping to set up a static function on the publisher that the actor can call, like this for example.

static UPublisherComponent* Create(UObject* Owner, void (UObject::* DelegateFunction)(int32& ID));

The above code doesn’t actually work, but its what I’m trying to achieve. The actor could then make a call like this in BeginPlay.

MyPublisher = UPublisherComponent::Create(this, &AMyActor::MyDelegateFunction);

The actor would then have a function like this.

void AMyActor::MyDelegateFunction(int32& ID)
{
}

When I do try to use the above code I get the following error.
UPublisherComponent UPublisherComponent::Create(UObject ,void (__cdecl UObject:: )(int32 &))’: cannot convert argument 2 from 'void (__cdecl AMyActor:: )(int32 &)’ to ‘void (__cdecl UObject::* )(int32 &)’

Well, it would be pretty simple to get rid of the dependency on USubscriber in the publisher: just turn BindUObject and BindUClass into templates:

UCLASS()
class UPublisher : public UObject
{
	GENERATED_BODY()

public:
	FMyDelegate MyDelegate;
	
	template<typename Subscriber>
	void BindUObject(Subscriber* Subscriber, float (Subscriber::* GetTime)(int Time));

	template<typename Subscriber>
	void BindUClass(UClass* Class, float (Subscriber::* GetTime)(int Time));
};


template<typename Subscriber>
void UPublisher::BindUObject(Subscriber* InSubscriber, float (Subscriber::* GetTime)(int Time))
{
	if (InSubscriber)
	{
		MyDelegate.BindUObject(InSubscriber, GetTime);
	}
	if (MyDelegate.IsBound())
	{
		UE_LOG(LogTemp, Warning, TEXT("The value of 'MyDelegate.Execute(2)' is: %f"), MyDelegate.Execute(2));
	}

}

template<typename Subscriber>
void UPublisher::BindUClass(UClass* Class, float (Subscriber::* GetTime)(int Time))
{
	Subscriber* MySubscriber{ NewObject<Subscriber>(this, Class) };
	if (ensure(MySubscriber))
	{
		MyDelegate.BindUObject(MySubscriber, GetTime);
	}
	if (ensure(MyDelegate.IsBound()))
	{
		UE_LOG(LogTemp, Warning, TEXT("The value of 'MyDelegate.Execute(2)' is: %f"), MyDelegate.Execute(2));
	}
}

Then you can use BindUClass and BindUObject for any kind of object/class and corresponding member function. E.g., the following works for a class UFooBar that is unrelated to the original USubscriber:

	Publisher->BindUClass(USubscriber::StaticClass(), &USubscriber::MyDelegateFunction);
	Publisher->BindUClass(USlowSubscriber::StaticClass(), &USubscriber::MyDelegateFunction);
	Publisher->BindUClass(UFooBar::StaticClass(), &UFooBar::SomethingDifferent);

If I understand correctly that should be sufficient for what you want to achieve.

Ah, templates, yes that seems to solve half the problem. Unfortunately the BindUObject on the delegate complains.

Here what my test function looks like.

template<typename ActorType>
static UPublisherComponent* Create(UObject* Owner, void (ActorType::* DelegateFunction)(int32& ID))
{
    UPublisherComponent* Component;
    // Bunch of other code here for setting up the component...
    Component->DelegateFunction.BindUObject(Owner, DelegateFunction);
    return Component;
}

When I call this function, I get the following error on the BindUObject line. Any other ideas up your sleeve?

void TBaseDelegate<TTypeWrapper<void>,int32 &>::BindUObject<UObject,>(UserClass *,void (__cdecl UObject::* )(int32 &) const)': cannot convert argument 2 from 'void (__cdecl AMyActor::* )(int32 &)' to 'void (__cdecl UObject::* )(int32 &)

Hmm, do you really need the type of Owner to be different from ActorType*? In that case I’m afraid you’re out of luck in C++. Even if you could somehow trick the compiler into accepting your code (by casting in the body of the function or something equally nasty) I’m pretty certain that converting member pointers to anything else (or applying them to anything that is not an instance of their defining class) leads to undefined behavior.

OK, there is one case in which passing in an UObject* Owner and then binding the delegate to that object should be fairly easy: If you want to have the UObject* type in the interface of your function to make it easier for callers to do something like BindUObject(GetSomeUObjectThatServesAsOwner(), ...) without having to explicitly cast the first argument. If the owner they pass in is always of the correct type for the delegate you can add a cast in the body of your function, and since the Unreal reflection system provides a dynamically type checked cast we don’t lose a lot:

Component->DelegateFunction.BindUObject(Cast<ActorType>(Owner), DelegateFunction);

I think defining the function with ActorType* Owner and having users explicitly cast to the correct type is the better solution in this case, since it makes it clearer from the interface of the function that you cannot pass in a first argument that is not compatible with the delegate.

But if you really need that kind of “duck typing” that allows you to invoke a member function of AActor on an UObject that might not be an actor (or similar constellations), you’re probably much better off with linking Unreal.js or UnrealEnginePython into your code and writing your components in a dynamic language. Especially since I’m guessing that if you need this kind of flexibility to initialize your actors, you will need it even more to implement their functionality.

Ah ha! You are right. No, I don’t need that to be UObject, I just simply forgot to change it to ActorType. It now works. Thanks so much for your help buddy.

Glad to hear it worked. Good luck with your project!