Download

Prevent Non-Default TSet from Garbage Collection

How are we supposed to keep a custom TSet alive? By custom, I mean TSet that has a user-specified KeyFuncs.
UPROPERTY() only supports default TSet, and it seems that AddReferencedObjects too.
As a result, my TSet are destroyed as soon as they stop containing a referenced object.

Here’s a short example.

  1. A class UMyIndexedObject that will be used as a key for the TSet


UCLASS(Blueprintable)
class UMyIndexedObject : public UObject
{
	GENERATED_BODY()

private:
	UPROPERTY()
	FString MyIndex; // the index key

public:
	FString GetIndex() const { return MyIndex; }
	//...
}


  1. A custom KeyFuncs for that class


struct FMyKeyFuncs : BaseKeyFuncs<UMyIndexedObject*, FString>
{
	typedef FString KeyInitType;
	static KeyInitType GetSetKey(const UMyIndexedObject* Obj) { return Obj->GetIndex(); }
	static bool Matches(KeyInitType A, KeyInitType B) { return A == B; }
	static uint32 GetKeyHash(KeyInitType Key) { return GetTypeHash(Key); }
};


  1. And a random class to declare and use the TSet:


UCLASS()
class USomeClass : public UObject
{
	GENERATED_BODY()

protected:
	UPROPERTY() // <--- fails here
	TSet<UMyIndexedObject*, FMyKeyFuncs, FDefaultAllocator> UnstableSet;
	//...
};


Problems here:

  • I can’t compile with UPROPERTY.
  • Compile error message is wrong (it says I cannot use a non-default Allocator, while I’m actually using a non-default KeyFuncs.
  • Non overload function found when trying to use AddReferencedObjects (seems to only support default TSet, like UPROPERTY)

So how are we supposed to use this nice custom KeyFuncs feature of TMap/TSet without loosing the whole Set to the garbage collector?

The object referencer overload that takes a TSet is just for convenience. If they didn’t make that templated on the set template parameters, then you can simply iterate over your set and pass each object to the referencer individually.

Some of the sets are empty, so I can’t reference the content :confused:
I’m trying with shared pointers/ref.

Why is that an issue? If the set is empty, then the iteration will do nothing - which is correct, because at that point there are no objects to reference.

Remember it’s not the set itself that is being garbage collected, only the UObjects within it. Shared pointers are completely unrelated to UObject garbage collection, so wrapping the set itself in one will have no effect.

Solved.
A nifty bug in the code led me to the wrong assumption the TSet was garbage collected. It was not.
Sorry for the bother.