Can not get value of TMap by enum key in c++ [Solved]

I have this enum class

UENUM(BlueprintType, Blueprintable)

enum class EResourceType : uint8
{
    Default  UMETA(DisplayName = "Default"),
    Energy  UMETA(DisplayName = "Energy"),
    Ore  UMETA(DisplayName = "Ore"),
    Metal  UMETA(DisplayName = "Metal")
};

In my other class I created a TMap

UPROPERTY(EditAnywhere, BlueprintReadWrite)
    TMap<EResourceType, int32> ResourcesLeft;

The problem is, when I try to find value by key in a loop

for(auto& Resource : ResourcesLeft)
    {
       SomeVar = ResourcesLeft.Find(Resource.Key)
    }

SomeVar then contains really wierd values, like -809933545 or 70476576

but if I go with just Resource.Value it returns the correct value.

Please help me understand what is wrong with my code.
Thanks.

Hi lastenemy

Since I cannot see how you’re initializing the variables it is difficult to tell exactly what is going wrong with your code. However, here is a very quick mock-up that works. Try to compare it with your code.

//Header
//----------------------------

UENUM(Blueprintable)
enum EResourceType
{
	default,
	metal,
	wood,
	brick
};

UCLASS()
class ANSWERHUB_API ATestActor : public AActor
{
	GENERATED_BODY()
	
public: 
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
	TMap<TEnumAsByte<EResourceType>, int32> resources;

public:	
	// Sets default values for this actor's properties
	ATestActor();

protected:
	virtual void BeginPlay() override;

};




//CPP
//----------------------------

// Sets default values
ATestActor::ATestActor()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = false;

}


void ATestActor::BeginPlay()
{
	Super::BeginPlay();

	//Initialise the resources map and add some default values
	resources.Add(EResourceType::brick, 10);
	resources.Add(EResourceType::metal, 20);
	resources.Add(EResourceType::wood, 30);

	//Loop through each of the resource entries 
	for (auto& resource : resources)
	{
		//Local variable to read the KEY of the current element
		TEnumAsByte<EResourceType> resourceKey = resource.Key;

		//Local variable to read the VALUE of the current element
		int32 resourceValue = resource.Value;

		//Print out the locally stored data for the current KEY and VALUE
		UE_LOG(LogTemp, Log, TEXT("The key is %s, the value is %d"), *UEnum::GetValueAsString(resourceKey.GetValue()), resourceValue);
	}

}

Hope this helps. If you have any questions, let me know.

Thanks

Alex

Thanks for the quick reply! I’ll give it a try and will let you know if it helps.


Nice, your example works, thanks. But could you please explain why result of resource.Value is correct and if I change it to
resources.Find(resourceKey) it gives me weird numbers?
Because that’s what’s documentation says about Find() function.

Find the value associated with a specified key.

Returns a pointer to the value associated with the specified key, or nullptr if the key isn’t contained in this map. The pointer is only valid until the next change to any key in the map.

Doesn’t that mean exactly what I’m doing - getting a value based on a key? And I definitely do not modify any of the map’s keys between

int32* resourceValue = resources.Find(resourceKey);

and

UE_LOG(LogTemp, Log, TEXT("The key is %s, the value is %d"), *UEnum::GetValueAsString(resourceKey.GetValue()), resourceValue);

I’ll explain why I am trying to do this with Find() function instead of just getting the Value directly.
I want to compare recources left with the resources cost of the building and return bool of comparison.
I got 2 TMap of the same type, 1 for ResourcesLeft, the other one for resources cost of a building.

Here is the function I’m stuck with

bool ATDPlayerControllerBase::IsEnoughResourcesForBuild(TMap<EResourceType, int32> ResourcesCost;)
{
    /*
     TMap<EResourceType, int32> ResourcesCost;
     is created in the Editor and  passed as an arguments to the function, 
     TMap<EResourceType, int32> ResourcesLeft;
      is a public member of the class and is set in the Editor as well. And  I've checked - both of them contain correct values
    */
    
    for(auto& ResourceCost :ResourcesCost)
    {
          if(ResourceCost.Value > ResourcesLeft.Find(ResourceCost.Key))
         {
             return false;
         }
    }
    return true;

}

Well, I was able to achieve the desired behavior.

I just need to use FindRef() instead of Find(), and those weird numbers was the address of the value.

Hi lastenemy

Unfortunately epic have got their meanings the wrong way round with the Find() and the FindRef() methods in TMap.

TMap::Find() will return a pointer (address in memory).
TMap::FindRef() will return the value assigned.

If you are want to retrieve a value from a pointer, you need to use the * symbol.
I have amended my previous example to demonstrate. Note I called my second map “inventory” just so it’s easier to distinguish.

Here is the output in my log:

LogTemp: brick has a value less than the inventory

LogTemp: metal has a value equal or greater than the inventory

LogTemp: wood has a value equal or greater than the inventory

Code:

void ATestActor::BeginPlay()
{
	Super::BeginPlay();

	resources.Add(EResourceType::brick, 10);
	resources.Add(EResourceType::metal, 20);
	resources.Add(EResourceType::wood, 30);

	inventory.Add(EResourceType::brick, 30);
	inventory.Add(EResourceType::metal, 20);
	inventory.Add(EResourceType::wood, 10);

	for (auto& resource : resources)
	{
		TEnumAsByte<EResourceType> resourceKey = resource.Key;
		int32 resourceValue = resource.Value;

		if (resourceValue < *inventory.Find(resourceKey))
		{
			UE_LOG(LogTemp, Log, TEXT("%s has a value less than the inventory"), *UEnum::GetValueAsString(resourceKey.GetValue()));
		}
		else
		{
			UE_LOG(LogTemp, Log, TEXT("%s has a value equal or greater than the inventory"), *UEnum::GetValueAsString(resourceKey.GetValue()));
		}
	}
}

Hope this clears things up for you.

Good luck

Alex