Structures, &, FTimerDelegate, and Functions?

Ive set up a Structure with which to track and modify a bunch of variables.
Certain functions use Reference arguments to both check and change values in the struct.
I also use an FTimerDelegate to call a function when a timer ends, where one of the arguments is a Reference to this struct.
An example of use:



Declared in .h:
FCustomData GLOBALDataStruct;

Tick()
{
	if (GetWorldTimerManager().TimerExists(Timing_ThingA))
	{
		FTimerDelegate TimerDelegateDoThingA;
		TimerDelegateDoThingA.BindUFunction(this, FName("DoThingA"), GLOBALDataStruct, 1);
		GetWorldTimerManager().SetTimer(Timing_ThingA, TimerDelegateDoThingA, 10.0F, false);
	}
}

DoThingA(FCustomData& InDataStruct, int32 InInt)
{
	if (InInt == 0) InDataStruct.IncrementInt = 0;
	DoThingB(InDataStruct, InInt);
}

DoThingB(FCustomData& InDataStruct, int32 InInt)
{
	if (InInt == 1)
	{
		InDataStruct.IncrementInt++;
		GEngine->AddOnScreenDebugMessage(-1, 10.0F, FColor::Yellow, "(IncrementInt)  " + FString::FromInt(InDataStruct.IncrementInt));
	}
}


The problem Im seeing is that the AddOnScreenDebugMessage() always displays: “(IncrementInt) 1”
Shouldn’t it be increasing by 1 every ten seconds?

During my Testing I changed the code to:



Declared in .h:
FCustomData GLOBALDataStruct;

Tick()
{
	if (GetWorldTimerManager().TimerExists(Timing_ThingA))
	{
		FTimerDelegate TimerDelegateDoThingA;
		TimerDelegateDoThingA.BindUFunction(this, FName("DoThingA"), GLOBALDataStruct, 1);
		GetWorldTimerManager().SetTimer(Timing_ThingA, TimerDelegateDoThingA, 10.0F, false);
	}
}

DoThingA(FCustomData& InDataStruct, int32 InInt)
{
	if (InInt == 0) InDataStruct.IncrementInt = 0;
	DoThingB(GLOBALDataStruct, InInt);
}

DoThingB(FCustomData& InDataStruct, int32 InInt)
{
	if (InInt == 1)
	{
		InDataStruct.IncrementInt++;
		GEngine->AddOnScreenDebugMessage(-1, 10.0F, FColor::Yellow, "(IncrementInt)  " + FString::FromInt(InDataStruct.IncrementInt));
	}
}


Which resulted in it increasing every ten seconds as intended.
So, I just want to confirm exactly whats going on:
Using DoThingA()'s FCustomData& InDataStruct, in the call to DoThingB() doesn’t point back to GLOBALDataStruct?
Can I not pass struct References through multiple layers of functions like that? or is it the FTimerDelegate that is breaking it?

Any help is appreciated.
Sorry if this ends up being a matter of basic programming knowledge, being self taught(through practice rather than book study) there are probably a bunch of areas of basic programming knowledge Im missing…

1 Like

Not a basic programming thing, just a limitation of UE4 delegates that there’s basically no info on.

Because the delegate signature doesn’t contain any parameters, your arguments are considered payload data - they’re set at the point of binding, not at the point of execution. So anything you pass into the Bind function after the target object and function is just stored inside the delegate and passed along to the target function when the delegate fires. Because payload can be anything and it’s only declared at the point of binding (there’s no predeclared signature for it anywhere), UE4 just assumes all payload parameters are to be stored by value (copy).

As far as I know, UE4 has no equivalent of std::ref to work around this, but you have the option of just passing by pointer instead - pass in &GLOBALDataStruct and change your function signature to use FCustomData*.

2 Likes

Thank you.
Knowing it is a delegate limitation will allow me to work around that more effectively.