How to make a class template UPROPERTY-compatible?

If I define a class template like:

template<typename ValueType>
class TMyDataStructure
{
	// ...
};

and then try to use it as a UPROPERTY:

UPROPERTY(EditAnywhere, BlueprintReadWrite)
TMyDataStructure<int32> MyDataStructure; // <-- ERROR

It gives error:

Error: Unrecognized type ‘TMyDataStructure’ - type must be a UCLASS, USTRUCT, UENUM, or global delegate.

But if I use (for example) TMap it works:

UPROPERTY(EditAnywhere, BlueprintReadWrite)
TMap<int32, int32> MyMap; // <-- OK

What is TMap doing that TMyDataStructure isn’t? The definition of TMap isn’t annotated with a U-macro or anything as far as I can see?

Is there some way to define TMyDataStructure so that it will work as a UPROPERTY ?

1 Like

As far as I know the build tool does not support templates on UCLASS, UPROPERTY, UFUNCTION etc. but you can have a template function which is not UFUNCTION within a normal UCLASS.

  • Also have to mark TMyDataStructure as USTRUCT.

Depends on what you need, but a workaround could be to create a base struct without this property and use it as a base class for structs which add the property of varying datatype.

USTRUCT(BlueprintType)
struct FS_BaseStruct {
	GENERATED_BODY()

	UPROPERTY(BlueprintReadWrite, EditAnywhere)
		bool bDoThis;

	UPROPERTY(BlueprintReadWrite, EditAnywhere)
		bool bDoThat;

	// Initialize
	FS_BaseStruct()
		: bDoThis (false)
		, bDoThat (false)
	{}
};

USTRUCT(BlueprintType)
struct FS_SpecializedStruct : public FS_BaseStruct {
	GENERATED_BODY()

	UPROPERTY(BlueprintReadWrite, EditAnywhere)
		FString YourValue;

	// Initialize
	FS_SpecializedStruct()
		: YourValue(TEXT("Hello"))
	{}
};


// Some class:

UPROPERTY(EditAnywhere)
	FS_SpecializedStruct X;
2 Likes

TArray and TMap (for example) are templates and can be the type of a UPROPERTY, so there must be some way to do it. I’m sure they are getting special treatment via some mechanism from UBT etc. I’m just wondering what that mechanism is, and what are the steps to giving TMyDataStructure that special treatment too.

1 Like

Most likely the build tool handles this, not sure.
Container classes are some special cases especially with Blueprints.

The container nodes on the blueprint graph are handled differently as well (scan source files for “CustomThunk”), for example KismetArrayLibrary.h :

UFUNCTION(BlueprintCallable, CustomThunk, meta=(DisplayName = "Add", CompactNodeTitle = "ADD", ArrayParm = "TargetArray", ArrayTypeDependentParams = "NewItem", AutoCreateRefTerm = "NewItem"), Category="Utilities|Array")
static int32 Array_Add(const TArray<int32>& TargetArray, const int32& NewItem);

The documentation of CustomThunk on UFUNCTION is

The UnrealHeaderTool code generator will not produce a thunk for this function; it is up to the user to provide one with the DECLARE_FUNCTION or DEFINE_FUNCTION macros.
2 Likes

TMap and TArray declarations don’t do anything per se. but the engine has hardcoding all over the place to handle them.

For example UnrealHeaderTool is hardcoded to parse TArrays and TMaps along with other primitive types :

And also a few wrapper types for objects :

In the reflection system these containers have specific FProperty implementations : FArrayProperty and FMapProperty.

In the editor code, graph pins have hardcoded support for those container types :

image

And finally as Seda pointed out, the blueprint nodes to handle these types use CustomThunk specifier to handle them as wildcard types. This part can be done with pretty much anything and is pretty much the only part you can realistically do. Here’s some good example for that.

2 Likes