UFUNCTIONs and Local Variable Issues

Hey all,

This is more of a rant, but I am interested in some workarounds.

I’ve been running into an inconvenience in Unreal when it comes to local scope. C++ in particular, doesn’t like say Player being defined in a class and then later as a parameter in a function. Kind of annoying, since local scope should provide some flexibility.

Like:

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "References")
class ASQPlayer* Player;

UFUNCTION(BlueprintCallable)
void AddPlayerToActiveList(ASQPlayer* Player);

The above is not allowed because of shadowing restrictions. That’s annoying, since the one in local scope (or function scope) shouldn’t create ambiguity.

So, then player lower case is doable since case sensitivity is a thing. I know a lot of programmers like to use underscores _player or dashes -player for this. However, UFUNCTIONs get even pickier. If two functions share the same variables in the parameter list, it’ll also throw an error.

Is it just me, or does it get exhausting when trying to write functions and having to come up with a unique name for every new instance of a variable? It’s local scope, it shouldn’t matter.

Here are some examples:

Fine in C++ if not a UFUNCTION

void AddPlayerToActiveList(ASQPlayer* player);
void RemovePlayerFromActiveList(ASQPlayer* player);
void AddActionToActionQueue(ASQPlayer* player, ASQPlanet* planet, EBuildingTypes buildingType, int buildTime);

If a UFUNCTION, it gets angry…

UFUNCTION(BlueprintCallable)
void AddPlayerToActiveList(ASQPlayer* player);

UFUNCTION(BlueprintCallable)
void RemovePlayerFromActiveList(ASQPlayer* player);

UFUNCTION(BlueprintCallable)
void AddActionToActionQueue(ASQPlayer* player, ASQPlanet* planet, EBuildingTypes buildingType, int buildTime);

I guess in this case, I could do “PlayerToAdd” and 'PlayerToRemove" but I shouldn’t have to. How do you deal with this? I’d rather not have to make a unique name every time I need a temporary variable for something.

I don’t get what you mean. The function parameter is local scope sure but you can still access non-local scope variables, so obviously you cannot have two with the same name. Anyways, for function parameters I mostly use in/out keywords (InPlayer), or pointer (PlayerPtr) mostly for internal variables.

2 Likes

True, but FuncA won’t ever see FuncB. So, there should never be any interference even if you were to call FunA inside of FuncB or vice versa. In the case of class variable ambiguity, you simply use this.var to differentiate, which is perfectly reasonable in C#. If you don’t differentiate, C# assumes you want the one in local scope.

Example:

class MyClass
{
	Character* Player;

	void AssignPlayer(Character* Player)
	{
		this.Player = Player;
	}
}

Thanks for the tip on InPlayer and PlayerPtr. I might borrow that.

Sounds like two different issues here, unless you mixed up something..

ASQPlayer* Player;

void AddPlayerToActiveList(ASQPlayer* Player);

This is shadowing, which is generally bad practice because it creates ambiguity when reading code. While the compiler can determine which “Player” you are referring to based on scope (and based on whether you specify this->Player or not), for a code reader it is more difficult.
These rules ensure the UE4/5 source code is more readable.
You can generally disable specific rules for your own code. In this case, either use PRAGMA_DISABLE_SHADOW_VARIABLE_WARNINGS in your header file, or set ShadowVariableWarningLevel = WarningLevel.Off; in your Build.cs.


UFUNCTION(BlueprintCallable)
void AddPlayerToActiveList(ASQPlayer* player);

UFUNCTION(BlueprintCallable)
void RemovePlayerFromActiveList(ASQPlayer* player);

UFUNCTION(BlueprintCallable)
void AddActionToActionQueue(ASQPlayer* player, ASQPlanet* planet, EBuildingTypes buildingType, int buildTime);

This is not shadowing, and should compile perfectly fine (both with and without UFUNCTION).
Unless of course you also have a variable “player” as class member… in which case back to first point.
In that case, a typical way to work around it would be :

ASQPlayer* Player;

UFUNCTION(BlueprintCallable)
void AddPlayerToActiveList(ASQPlayer* InPlayer);

UFUNCTION(BlueprintCallable)
void RemovePlayerFromActiveList(ASQPlayer* InPlayer);

UFUNCTION(BlueprintCallable)
void AddActionToActionQueue(ASQPlayer* InPlayer, ASQPlanet* planet, EBuildingTypes buildingType, int buildTime);