quinta-feira, 31 de dezembro de 2020

TSet: Sets in C++

TSet is another type of container similar to TArray. Conceptually, a Set is an unordered list of unique elements. The TSet template has a parameter to allow duplicate elements and also has a function for sorting, but in this article, we will focus on the traditional use of a Set.

A TSet does not use an index. The element's value is used as the key. The elements of a TSet must be of the same type.

The example below creates a TSet with the name CreaturesByRegion to store elements of type FString. This TSet can be modified in the editor.

UPROPERTY(EditAnywhere, Category=SetExample)
TSet<FString> CreaturesByRegion;

Use the Add() function to add elements to a TSet in C++.

CreaturesByRegion.Add(TEXT("Ghoul"));
CreaturesByRegion.Add(TEXT("Necrophage"));
CreaturesByRegion.Add(TEXT("Werewolf"));

To see how many elements there are in a TSet, use the Num() function:

int32 NumberOfElements = CreaturesByRegion.Num();

Use the Contains() function to check if an element is part of a TSet:

bool bHasCreature = CreaturesByRegion.Contains(TEXT("Necrophage"));

To remove an element, use the Remove() function. If the element is not found, the function returns a value of 0.

int32 RemovedAmount = CreaturesByRegion.Remove(TEXT("Ghoul"));

To remove all items from a TSet, use the Empty() function: 

CreaturesByRegion.Empty();

TSet has some functions that perform operations with two Sets and return a different Set. These are the functions:

  • Union()Returns a TSet containing the elements of the two Sets. Duplicates will be removed.
  • Difference()Returns a TSet that contains the elements of the first Set that are not in the second Set.
  • Intersect()Returns a TSet containing only the elements that exist in the two Sets.

The code below shows the use of these functions.

TSet<int32> Set1;
TSet<int32> Set2;

Set1.Add( 3 );
Set1.Add( 5 );
Set1.Add( 7 );

Set2.Add( 4 );
Set2.Add( 5 );
Set2.Add( 6 );

TSet<int32> UnionResult = Set1.Union(Set2);
// UnionResult = [ 3, 4, 5, 6, 7 ] 

TSet<int32> DifferenceResult = Set1.Difference(Set2);
// DifferenceResult = [ 3, 7 ] 

TSet<int32> IntersectResult = Set1.Intersect(Set2);
// IntersectResult = [ 5 ]


Example usage:

Create a C++ class with the name TestTSet using the Actor class as the parent class. In the TestTSet.h file, add the declaration of the components and of the TSet:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TestTSet.generated.h"

UCLASS()
class TUTOPROJECT_API ATestTSet : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ATestTSet();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	UPROPERTY(VisibleAnywhere)
	USceneComponent* RootScene;

	UPROPERTY(VisibleAnywhere)
	UStaticMeshComponent* StaticMesh;
	
	UPROPERTY(EditAnywhere, Category=SetExample)
	TSet<FString> CreaturesByRegion;
};

In the BeginPlay() function, we will write on the screen the name of the TSet elements. A ranged-based for loop is used to iterate over the TSet elements. The contents of the TestTSet.cpp file look like this: 

#include "TestTSet.h"

ATestTSet::ATestTSet()
{
  // Set this actor to call Tick() every frame.
  PrimaryActorTick.bCanEverTick = true;

  RootScene = CreateDefaultSubobject<USceneComponent>("RootScene");
  RootComponent = RootScene;

  StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>("StaticMesh");
  StaticMesh->SetupAttachment(RootScene);
}

// Called when the game starts or when spawned
void ATestTSet::BeginPlay()
{
  Super::BeginPlay();

  FString Message;
  
  if(CreaturesByRegion.Num() > 0)
  {
    Message = FString::Printf(TEXT("Number of elements in the Set: %d"),
                              CreaturesByRegion.Num());

    if(GEngine)
    {		 
      GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, Message);
	  
      for (FString Creature : CreaturesByRegion)
      {
        Message = FString::Printf(TEXT("Creature Name: %s"), *Creature);
		
        GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, Message);
      }
    }	  
  }	    
}

// Called every frame
void ATestTSet::Tick(float DeltaTime)
{
  Super::Tick(DeltaTime);
}

Compile the C++ code and add an instance of TestTSet at the level. You can add elements in the TSet using the Details tab of the instance. Try adding a duplicate value. A message will appear saying that it is not allowed.


Start the game and see on the screen the names of the elements added to the TSet:


Table of Contents C++