TMap with struct

The example code for TMap using structs isn’t working. I am talking about the code here at the bottom of the page.

If you use the code from the example, you will get the error:


Unrecognized type 'FMyStruct' - type must be a UCLASS, USTRUCT or UENUM

So if I make the struct a USTRUCT I will receive the error:


USTRUCTs are not currently supported as key types.

Is it so that the documentation is outdated and there is no way to use structs?

I also have trouble following the documentation page, it seem to target more advanced use-cases thus require the using the BaseKeyFuncs template.

What I needed is just a simple map however, and after some heavy Google-ing I at least got my TMap that uses a USTRUCT as key to compile, just by overriding the ==operator and GetTypeHash().
Note I only got it to compile and I still need more testing to check if everything is working ok.

Here are the methods I defined inside my USTRUCT:

bool operator==(const FMyStruct& s) const
{
// return your == definition
}

friend FORCEINLINE uint32 GetTypeHash(const FMyStruct& s)
{
//return your hash definition
}

Would also like to know from more experienced users whether what I done above is correct, thanks.

Here is short sample for USTRUCT() used as Key in TMap<>



USTRUCT()
struct FMyStruct
{
 GENETERATED_USTRUCT_BODY()

 int32 ID;

 bool operator== (const FMyStruct& Other)
 {
   return int32 ID == Other.ID;
 }
 friend uint32 GetTypeHash (const FMyStruct& Other)
 {
   return GetTypeHash(Other.ID);
 }
}


This should work. You can replace int32 with any hashable type, which can be converted to numeric value. Like FName.

I am still getting


USTRUCTs are not currently supported as key types.

mind posting the full code of the struct?

This isn’t the exact code I am using, but this still wont compile.


USTRUCT()
struct FMyStruct
{
	GENERATED_BODY()

	int32 ID;

	bool operator== (const FMyStruct& Other)
	{
		return ID == Other.ID;
	}

	friend uint32 GetTypeHash(const FMyStruct& Other)
	{
		return GetTypeHash(Other.ID);
	}
};

UCLASS()
class AActorTest : public AActor
{
	GENERATED_BODY()

	UPROPERTY(VisibleAnywhere)
	TMap<FMyStruct, int32> hashmap;
};


ActorTest.h(30) : USTRUCTs are not currently supported as key types.

Have you tried using GENERATED_USTRUCT_BODY() instead of GENERATED_BODY() for your struct? I know they “replaced” GENERATED_UCLASS_BODY() with GENERATED_BODY() but I’m not sure if the macro is meant to be used for USTRCTs too.

Off-topic: This is the one thing that I hate most for UE4, many things have been deprecated / changed since release but the internet is flooded with legacy use cases / information while there is not a central place that can verify what is currently correct. (there is the source-code but I think its not realistic for an average user to dig through it on every aspect…)

I tried using GENERATED_USTRUCT_BODY() , does not change anything.

FYI: GENERATED_UCLASS_BODY() doesn’t creates the constructor, GENERATED_BODY() does.

I never thought of going into the source code for something this simple, even when there is a documentation page.
So I did a search for TMap and found this great example.

This is the code I came up with that works.



struct FMyStruct
{
	int32 ID;

	FMyStruct()
	{}

	friend bool operator==(const FMyStruct& first, const FMyStruct& second)
	{
		return (first.ID == second.ID);
	}
};

UCLASS()
class AActorTest : public AActor
{
	GENERATED_BODY()

	//UPROPERTY(VisibleAnywhere)
	TMap<FMyStruct, int32> hashmap;
};


Having a UPROPERTY() on the TMap doesn’t work apparently, it will throw the USTRUCT issue again.

You can definitely have UPROPERTY() on TMaps, but not UPROPERTY(VisibleAnywhere) :slight_smile:

Main purpose to use empty UPROPERTY() would be to prevent garbage collection if your values or keys have pointers to other UObjects…

If I have UPROPERTY() , I get the error:


Unrecognized type 'FMyStruct' - type must be a UCLASS, USTRUCT or UENUM

ahh that’s because of the struct as key… otherwise TMaps can be UPROPERTY()

Reflection system does not support TMap, so don’t place UPROPERTY(), you need to use USTRUCT() if you using structure as argument in function, because reflection system need to see structure in order for function argument to work in reflection system

TMap is exposed to reflection since 4.7 ;).

I had the same problem and none of the answers here helped me really. I searched a while in the internet but couldn’t get something run for me so I looked in the unreal source code and I found out how I can solve my problem.
I searched for “uint32 GetTypeHash(” and the first header with it was AssetEditorSelectedItem.h from unreal source code. I found out that I had to write a function in my struct with a
uint32 GetTypeHash() const
{
// write your hash generation here
(I found some hash generation in FCrc.h if you need a hash generation)
}
body. Outside the USTUCT I had to implement a function with
FORCEINLINE uint32 GetTypeHash(const ‘structname’& other)
{
return other.GetTypeHash();
}

that’s for the hash, the function with
bool operator==(const ‘structname’& other) const
{
}
worked for me fine.
I know this post is really outdated but I hope my answer can help.

This is the way to fix the issue in 4.25 build (launcher and GitHub both). Thanks for digging into the source code so I don’t have to :slight_smile:

I have been successfully using



UPROPERTY()
TMap<struct1, struct2> X


when struct1 has GetTypeHash() defined.

This works, too. And I understand it, because “TMap is similar to TSet in that its structure is based on hashing keys” therefore you need to somehow define the unique key value. However I found out that leaving the function basically blank will not provide you with the functionality needed, that means it will not consider two FStructs the same even those are the same. The workaround I found out is to define a simple rule to generate FString ID that is based on a combination of the most noticeable attributes of the FStruct (FName FirstName, FName LastName, for instance) and then matching the TMap key based on this FString ID. Creating new value for DominikPavlicek will show me the error because I have already defined this value.

Sorry…did the GetTypeHash() as note…didn’t mean to be exact.



USTRUCT(BlueprintType)
struct xyz_API FImapSlice
{
public:
GENERATED_BODY()

FAWIntVector coords;

UPROPERTY()
TMap<uint8, UAWIMap*> ImapSlice;


/** Equality operators */
bool operator==(const FImapSlice& Other) const
{
return coords == Other.coords;
}
bool operator!=(const FImapSlice& Other) const
{
return !(*this == Other);
}

/** Implemented so it can be used in Maps/Sets */
friend inline uint32 GetTypeHash(const FImapSlice& Key)
{
uint32 Hash = 0;

Hash = HashCombine(Hash, GetTypeHash(Key.coords));
return Hash;
}
};


1 Like