Unexpected Unreal Behaviour

Introduction

This is notes on Unreal Engine behaviour which might surprise.

  1. The Unreal Header Tool sometimes changes method parameter declarations
  2. The Unreal Header Tool is not fully satisfied by forward declarations

The Unreal Header Tool sometimes changes method parameter declarations

Given a class declared like this:

USTRUCT(BlueprintType)
struct EXAMPLE_API FActionContainer
{
	GENERATED_BODY()

};

UCLASS()
class EXAMPLE_API AMyCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AMyCharacter();

	UFUNCTION(BlueprintImplementableEvent, Category = "Interaction")
	void OpenExpandableItemActions(TArray<FActionContainer> actions);
};

The above code will not compile. In the output window we get this error:

1>MyCharacter.gen.cpp(71): error C2511: 
	'void AMyCharacter::OpenExpandableItemActions(const TArray<FActionContainer,FDefaultAllocator> &)': 
	overloaded member function not found in 'AMyCharacter'
1>MyCharacter.h(19): 
	note: see declaration of 'AMyCharacter'
1>MyCharacter.gen.cpp(75): 
	error C2352: 'UObject::FindFunctionChecked': a call of a non-static member function requires an object
1>Object.h: 
	see declaration of 'UObject::FindFunctionChecked'

What is happening here is that the Unreal Header Tool (UHT) is generating code into the file MyCharacter.gen.cpp, and the code it is generating has different parameter types than the code in our class declaration. The code in the generated .cpp file looks like this:

static FName NAME_AMyCharacter_OpenExpandableItemActions = FName(TEXT("OpenExpandableItemActions"));
void AMyCharacter::OpenExpandableItemActions(const TArray<FActionContainer>& actions)
{
	MyCharacter_eventOpenExpandableItemActions_Parms Parms;
	Parms.actions=actions;
	ProcessEvent(FindFunctionChecked(NAME_AMyCharacter_OpenExpandableItemActions),&Parms);
}

This code is generated to support the BlueprintImplementableEvent aspect of the OpenExpandableItemActions method, so that the method can be implemented in a blueprint rather than in c++.

The UHT generates code which uses the types which blueprint supports, which in this example are different from the types we used when declaring OpenExpandableItemActions. Specifically UHT changes the type of the actions parameter from TArray<FActionContainer> to const TArray<FActionContainer>&, i.e. it changes a value type to a const reference type.

The solution to this error is to change our header to define the method the way UHT wants, i.e. using a const reference like this:

UCLASS()
class GARBAGECOLLECTION_API AMyCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AMyCharacter();

	UFUNCTION(BlueprintImplementableEvent, Category = "Interaction")
	void OpenExpandableItemActions(const TArray<FActionContainer>& actions);
};

The Unreal Header Tool is not fully satisfied by forward declarations

If you generate a first person game project with C++ from the Unreal Editor you create a file called Character.h. This contains forward declares the type USkeletalMeshComponent and then uses it like this:

...
class USkeletalMeshComponent;
...
UCLASS(config=Game)
class AProjectCharacter : public ACharacter
{
	GENERATED_BODY()

	/** Pawn mesh: 1st person view (arms; seen only by self) */
	UPROPERTY(VisibleDefaultsOnly, Category=Mesh)
	USkeletalMeshComponent* Mesh1P;
	...
}

This makes it look as though when UHT processes the UPROPERTY macro it uses the forward declaration to resolve the USkeletalMeshComponent type. But it does not. If you change the USkeletalMeshComponent type to something which does not exist such as NonExistantClass like so:

...
class NonExistantClass;
...
UCLASS(config=Game)
class AProjectCharacter : public ACharacter
{
	GENERATED_BODY()

	/** Pawn mesh: 1st person view (arms; seen only by self) */
	UPROPERTY(VisibleDefaultsOnly, Category=Mesh)
	NonExistantClass* Mesh1P;
	...
}

This will not compile, it fails with this error:

1>ProjectCharacter.h(28): error : Unrecognized type 'NonExistantClass' - type must be a UCLASS, USTRUCT, 
                  UENUM, or global delegate.

Using a forward declaration to a non-existant class would work in straight C++, but the UHT must be parsing the engine and project code and maintaining a list of UCLASS, USTRUCT, UENUM types and failing if it does not find the specified type.