TSet DefaultKeyFuncs

I have a class that inherits from UObject. I’d like to store it in a set and that works fine. The problem I have, is that I want to lookup based on a member in the object (which is a gameplay tag).

I have been coding for years and it’s embarrassing to admit this but I have no idea how to do this after reading the documentation.
I’ve read this but I’m a bit lost. I’d love to have an example but a lot of the code documentation forgets about examples…

As long as a type has an `operator==` and a non-member `GetTypeHash` overload, TSet can use it, since the type is both the element and the key. However, it may be useful to use types as keys where it is undesirable to overload those functions. In these cases, you can provide your own custom `DefaultKeyFuncs`. To create `KeyFuncs` for your key type, you must define two typedefs and three static functions, as follows:

* `KeyInitType` — Type used to pass keys around. Usually drawn from ElementType template parameter.
* `ElementInitType` — Type used to pass elements around. Also usually drawn from ElementType template parameter, and therefore identical to KeyInitType.
* `KeyInitType GetSetKey(ElementInitType Element)` — Returns the key of an element. For sets, this is usually just the element itself.
* `bool Matches(KeyInitType A, KeyInitType B)` — Returns `true` if `A` and `B` are equivalent, `false` otherwise.
* `uint32 GetKeyHash(KeyInitType Key)` — Returns the hash value of `Key`.

`KeyInitType` and `ElementInitType` are typedefs to the normal passing convention of the key/element type. Usually, these will be a value for trivial types and a const reference for non-trivial types. Remember that the element type of a set is also the key type, which is why `DefaultKeyFuncs` uses only one template parameter, `ElementType`, to define both.

`TSet` assumes that two items that compare equal using `Matches` (in `DefaultKeyFuncs`) will also return the same value from `GetKeyHash` (in `KeyFuncs`).

What I gather from the documentation:
I can either add the GetKeyHash, Matches, GetSetKey, ElementInitType, KeyInitType or overload those two functions. In my case, overriding these works just fine. (Would still like to know what to do when that’s not the case).

The == operator must be overridden. Done.
The GetTypeHash returns a uint32, I assumed I can return the GetTypeHash from the FGameplayTag struct. So
FORCEINLINE uint32 GetTypeHash(UMyObject& obj) { return GetTypeHash(obj.TheGameplayTag); }

  • A set with an optional KeyFuncs parameters for customizing how the elements are compared and searched.
  • E.g. You can specify a mapping from elements to keys if you want to find elements by specifying a subset of
  • the element type. It uses a TSparseArray of the elements, and also links the elements into a hash with a
  • number of buckets proportional to the number of elements. Addition, removal, and finding are O(1).
    typename InElementType,
    typename KeyFuncs /= DefaultKeyFuncs/,
    typename Allocator /= FDefaultSetAllocator/

class TSet

So now, I need to declare my TSet:
TSet<TSubclassOf, ???, ???> SetAcquiredAbilities;

So do I also need to create a function for how they are compared? UMyObject::MatchesMyObject()
Then the Allocator… no idea what to do with that. Is that the GetTypeHash?

Could some one write up an example, explain this to me like I’m 5, or PM me to set up a conversation about it? I don’t mind paying for a very good answer and some Q&A.

If you want to lookup a value based on a key then either use TMap (this is exactly what it’s designed for) - or TArray and use FindByPredicate() in this case. Unless you’re storing thousands of items or doing this very often, the performance difference will be negligible.

The TSet is storing a pointer to a UObject, not the UObject itself - that’s likely why your custom hash function doesn’t work. Since you can’t define a custom hashing method for a pointer to your UObject, you need to use a more suitable container.

It’s generally not a good idea to override hashing functions or operators on UObjects’ anyway.

I’m not actually implementing this. My problem was more that I read the comments and documentation and came away with very little understanding of how I would go about it.