Developer_Bastian

Expose TMap to Blueprint - Sorting, Filtering, Parallel Iterations

Feb 7th, 2024 (edited)
137
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.91 KB | Gaming | 0 0
  1. // Developer Bastian © 2024
  2. // License Creative Commons DEED 4.0 (https://creativecommons.org/licenses/by-sa/4.0/deed.en)
  3. // Tutorial video at https://youtu.be/YNt_3m4KYos
  4. // Part of an Unreal Basics video tutorial series at: https://bit.ly/Unreal_Basics_en
  5.  
  6. #pragma once
  7.  
  8. #include "CoreMinimal.h"
  9. #include "CoreTypes.h"
  10. #include "UObject/NoExportTypes.h"
  11. #include "Runtime/Core/Public/Async/ParallelFor.h"
  12. #include "Misc/Guid.h"
  13. #include "Misc/SpinLock.h"
  14. #include "Containers/Map.h"
  15. #include "Timer.h"
  16.  
  17. #include "TMap.generated.h"
  18.  
  19. #pragma region Enum for Sorting
  20. /**
  21.  * Enum to structure sorting for FTSetTestStruct.
  22.  */
  23. UENUM(BlueprintType)
  24.     enum class ETestMapSorting : uint8 {
  25.         E_NumberAsc     UMETA(DisplayName = "Number - Ascending"),
  26.         E_NumberDesc    UMETA(DisplayName = "Number - Descending"),
  27.         E_NameAsc       UMETA(DisplayName = "Name - Ascending"),
  28.         E_NameDesc      UMETA(DisplayName = "Name - Descending")
  29.     };
  30. #pragma endregion Enum for Sorting
  31.  
  32. #pragma region Struct
  33. /**
  34.  * Struct to showcase the TCircularQueue.
  35.  */
  36. USTRUCT(BlueprintType)
  37. struct FMapTestStruct
  38. {
  39. public:
  40.     GENERATED_USTRUCT_BODY()
  41.  
  42.     UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Test Struct")
  43.     FGuid Guid;
  44.    
  45.     UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Test Struct")
  46.     FString Name;
  47.  
  48.     UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Test Struct")
  49.     int32 Number;
  50.  
  51.     FMapTestStruct() : Name(""), Number(0)
  52.     {
  53.     }
  54.    
  55. #pragma region Sorting
  56.     static bool CompareNumberAscending(const FMapTestStruct& A, const FMapTestStruct& B)
  57.     {
  58.         return A.Number < B.Number;
  59.     }
  60.  
  61.     static bool CompareNumberDescending(const FMapTestStruct& A, const FMapTestStruct& B)
  62.     {
  63.         return A.Number > B.Number;
  64.     }
  65.  
  66.     static bool CompareNameAscending(const FMapTestStruct& A, const FMapTestStruct& B)
  67.     {
  68.         return A.Name < B.Name;
  69.     }
  70.  
  71.     static bool CompareNameDescending(const FMapTestStruct& A, const FMapTestStruct& B)
  72.     {
  73.         return A.Name > B.Name;
  74.     }
  75. #pragma endregion Sorting
  76. };
  77. #pragma endregion Struct
  78.  
  79. /**
  80.  * Delegates to indicate queue changes
  81.  */
  82. DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMapChanged, FMapTestStruct, changed);
  83.  
  84.  
  85. /**
  86.  * Class to encapsulate TMap - A Key-Value Store
  87.  */
  88. UCLASS(BlueprintType, Transient)
  89. class UTMap : public UObject
  90. {
  91.     GENERATED_BODY()
  92.  
  93. public:
  94.  
  95.     UTMap()
  96.     {}
  97.  
  98.     ~UTMap()
  99.     {
  100.         this->BA_Map.Reset();
  101.     }
  102.  
  103.     #pragma region Delegates
  104.  
  105.     UPROPERTY(BlueprintAssignable, Category = "BA Container - Map"
  106.         , meta = (ToolTip = "Delegate to indicate enqueue event on queue"))
  107.     FOnMapChanged OnMapAdd_Delegate;
  108.  
  109.     UPROPERTY(BlueprintAssignable, Category = "BA Container - Map"
  110.         , meta = (ToolTip = "Delegate to indicate dequeue event on queue"))
  111.     FOnMapChanged OnMapDelete_Delegate;
  112.  
  113. #pragma endregion Delegates
  114.  
  115. private:
  116.     TMap<FGuid, FMapTestStruct> BA_Map;
  117.  
  118. public:
  119.  
  120.     #pragma region Public Functions
  121.  
  122.     #pragma region Add and Remove
  123.     UFUNCTION(BlueprintCallable, Category = "BA Container - Map"
  124.         , meta = (CompactNodeTitle = "Add Item"
  125.             , ToolTip = "Add one Key-Value pair to the map"))
  126.     FORCEINLINE void Map_Add(UPARAM(ref) FMapTestStruct& Value, bool Broadcast)
  127.     {
  128.         BA_Map.Add(Value.Guid, Value);
  129.         if (Broadcast)
  130.             this->OnMapAdd_Delegate.Broadcast(Value);
  131.     }
  132.  
  133.     UFUNCTION(BlueprintCallable, Category = "BA Container - Map"
  134.         , meta = (CompactNodeTitle = "Remove Item"
  135.             , ToolTip = "Remove all associations between the specified key and value from the multi map"))
  136.     FORCEINLINE FMapTestStruct Map_Remove(UPARAM(ref) FGuid& Key, bool Broadcast)
  137.     {
  138.         FMapTestStruct tmpValue;
  139.         bool found = this->BA_Map.RemoveAndCopyValue(Key, tmpValue);
  140.         if (Broadcast && found)
  141.             this->OnMapDelete_Delegate.Broadcast(tmpValue);
  142.         return tmpValue;
  143.     }
  144. #pragma endregion Add and Remove
  145.  
  146.     #pragma region Map Misc
  147.     UFUNCTION(BlueprintCallable, Category = "BA Container - Map"
  148.         , meta = (CompactNodeTitle = "Number of values"
  149.             , ToolTip = "Returns the number of values within this map"))
  150.     FORCEINLINE int32 Map_NumberOfValues()
  151.     {
  152.         return this->BA_Map.Num();
  153.     }
  154.  
  155.     UFUNCTION(BlueprintCallable, Category = "BA Container - Map"
  156.         , meta = (CompactNodeTitle = "Empty"
  157.             , ToolTip = "Empties the map - set NewCapacity to zero if you dont need to reserve space for new content, otherwise provide the expected capacity"))
  158.     FORCEINLINE void Map_Empty(int32 NewCapacity)
  159.     {
  160.         this->BA_Map.Empty(NewCapacity);
  161.     }
  162. #pragma endregion Map Misc
  163.  
  164.     #pragma region Get Values and Keys
  165.     UFUNCTION(BlueprintCallable, Category = "BA Container - Map"
  166.         , meta = (CompactNodeTitle = "Get Value"
  167.             , ToolTip = "Returns a values matching the given key - or an empty default struct if key is not found"))
  168.     FORCEINLINE FMapTestStruct Map_GetValue(UPARAM(ref) FGuid& Key)
  169.     {
  170.         return this->BA_Map.FindRef(Key);
  171.     }
  172.  
  173.     UFUNCTION(BlueprintCallable, Category = "BA Container - Map"
  174.         , meta = (CompactNodeTitle = "Get Keys"
  175.             , ToolTip = "Gets all keys of the map"))
  176.     FORCEINLINE TArray<FGuid> Map_GetKeys()
  177.     {
  178.         TArray<FGuid> keys;
  179.         this->BA_Map.GenerateKeyArray(keys);
  180.         return keys;
  181.     }
  182.  
  183.     UFUNCTION(BlueprintCallable, Category = "BA Container - Map"
  184.         , meta = (CompactNodeTitle = "Get Values"
  185.             , ToolTip = "Gets all values of the map"))
  186.     FORCEINLINE TArray<FMapTestStruct> Map_GetValues()
  187.     {
  188.         TArray<FMapTestStruct> values;
  189.         this->BA_Map.GenerateValueArray(values);
  190.         return values;
  191.     }
  192.  
  193.     UFUNCTION(BlueprintCallable, Category = "BA Container - Map"
  194.         , meta = (CompactNodeTitle = "Filter Cities"
  195.             , ToolTip = "Gets all cities with population larger than parameter"))
  196.     FORCEINLINE TMap<FGuid, FMapTestStruct> Map_FilterCities(int32 Population)
  197.     {
  198.         TMap<FGuid, FMapTestStruct> filtered = this->BA_Map.FilterByPredicate(
  199.             [Population](const TPair<FGuid, FMapTestStruct>& KvP)
  200.             {
  201.                 return KvP.Value.Number > Population;
  202.             }
  203.         );
  204.         return filtered;
  205.     }
  206. #pragma endregion Get Values and Keys
  207.  
  208.     #pragma region Sorting Values and Keys
  209. UFUNCTION(BlueprintCallable, Category = "BA Container - Map"
  210.         , meta = (CompactNodeTitle = "Value Sort"
  211.             , ToolTip = "Sort the values of the map using ETestMapSorting and static functions of the value struct"))
  212.     FORCEINLINE void Map_ValueSort(ETestMapSorting Sorting)
  213.     {
  214.         switch (Sorting)
  215.         {
  216.         case ETestMapSorting::E_NumberAsc:
  217.             this->BA_Map.ValueSort(FMapTestStruct::CompareNumberAscending);
  218.             break;
  219.         case ETestMapSorting::E_NumberDesc:
  220.             this->BA_Map.ValueSort(FMapTestStruct::CompareNumberDescending);
  221.             break;
  222.         case ETestMapSorting::E_NameAsc:
  223.             this->BA_Map.ValueSort(FMapTestStruct::CompareNameAscending);
  224.             break;
  225.         case ETestMapSorting::E_NameDesc:
  226.             this->BA_Map.ValueSort(FMapTestStruct::CompareNameDescending);
  227.             break;
  228.         default: break;
  229.         }  
  230.     }
  231.  
  232.     UFUNCTION(BlueprintCallable, Category = "BA Container - Map"
  233.         , meta = (CompactNodeTitle = "Sort Keys"
  234.             , ToolTip = "Sort the keys of the map"))
  235.     FORCEINLINE void Map_KeySort()
  236.     {
  237.         this->BA_Map.KeySort([](const FGuid& A, const FGuid& B)
  238.         {
  239.             return A.ToString() > B.ToString();
  240.         }
  241.         );
  242.     }
  243. #pragma endregion Sorting Values and Kesys
  244.  
  245.     #pragma region Iteration Examples
  246. UFUNCTION(BlueprintCallable, Category = "BA Container - Map"
  247.         , meta = (CompactNodeTitle = "Iterate Map"
  248.             , ToolTip = "Example to iterate over map values, adding a prefix to all Struct.Names"))
  249.     FORCEINLINE float Map_Iterate(UPARAM(ref) FString& Prefix)
  250.     {
  251.         // make parameter local
  252.         FString lPrefix = Prefix;
  253.         // for demonstration, we set a timer and report total time needed for operation
  254.         Timer t; t.Start();
  255.         TArray<FGuid> keys;
  256.         TArray<FMapTestStruct> values;
  257.         // get all keys from map and iterate       
  258.         this->BA_Map.GenerateKeyArray(keys);
  259.         for (FGuid& guid : keys)
  260.         {
  261.             FMapTestStruct* value = this->BA_Map.Find(guid);
  262.             if (value)
  263.                 value->Name.InsertAt(0, lPrefix);
  264.         }
  265.         return t.Stop();
  266.         // ==> took about 3200 CPU cycles
  267.     }
  268.  
  269.     UFUNCTION(BlueprintCallable, Category = "BA Container - Map"
  270.         , meta = (CompactNodeTitle = "Parallel Iterate Map"
  271.             , ToolTip = "Example to parallel iterate over map values, adding a prefix to all Struct.Names"))
  272.     FORCEINLINE float Map_ParallelIterate(UPARAM(ref) FString& Prefix)
  273.     {
  274.         // make parameter local
  275.         FString lPrefix = Prefix;
  276.         // for demonstration, we set a timer and report total time needed for operation
  277.         Timer t; t.Start();
  278.         // create vars
  279.         TArray<FGuid> keys;
  280.         TArray<FMapTestStruct> values;
  281.         // get all keys from map       
  282.         this->BA_Map.GenerateKeyArray(keys);
  283.         // Iteration
  284.         FCriticalSection Mutex;
  285.         ParallelFor(keys.Num(), [&, lPrefix](int32 i)
  286.             {
  287.                 FMapTestStruct* value = this->BA_Map.Find(keys[i]);
  288.                 if (value)
  289.                 {
  290.                     //FScopeLock Lock(&Mutex);
  291.                     value->Name.InsertAt(0, lPrefix);
  292.                 }
  293.             }, EParallelForFlags::None);
  294.         return t.Stop();
  295.         // ==> took about 330 CPU cycles without lock
  296.         // ==> took about 26000(!) CPU cycles with lock (uncomment "//FScopeLock Lock(&Mutex);")
  297.     }
  298. #pragma endregion Iteration Examples
  299.    
  300.  
  301. #pragma endregion Public Functions
  302. };
Add Comment
Please, Sign In to add comment