How to safely use template structs?

I have a template struct as such:

template <typename T>
struct FMyStruct {

   public:
    FMyStruct(UMyObject** NewMyObj, T DefaultValue) {
        MyObj = NewMyObj;
        Value = DefaultValue
    };

    void SetValue(T NewValue) {
        Value = NewValue;

        if (*MyObj)
            (*MyObj)->DoSomething();
    }

    T GetValue() const {  return Value; }


   private:
    T Value;
    UMyObject** MyObj;
};

And I’m using it in my custom Actor class as such:

// .h file
UCLASS()
class MYPROJECT_API AMyActor : public AActor
{
	GENERATED_BODY()
	
   public:	
	AMyActor();

	UPROPERTY()
	UMyObject * SomeObj;

	FMyStruct<bool> KeyA = FMyStruct<bool>(&SomeObj, true);
	FMyStruct<int> KeyB = FMyStruct<int>(&SomeObj, 10);
};


// .cpp file
AMyActor::AMyActor(){
    SomeObj = CreateDefaultSubobject<UMyObject>(TEXT("SomeObj"));
};

I used a pointer to a pointer UMyObject** MyObj since when assigning FMyStruct there is no guarantee that the SomeObj will have a value assigned to it yet

Is this the proper way to do this? Or will this cause any memory leaks, inconsistent behaviours or issues with the reflection system?

I’ve got to ask what is the purpose for this?

“reflection” (UPROPERTY, UFUNCTION) does not work with templates. This leads to a few problems like properties or methods not being usable with blueprints. I’ve never had to make a templated struct in all my time but this should also introduce the problem that “MyObj” on the struct can not be a UPROPERTY. UMyObject (the U prefix) means it’s a UObject, and UObject properties must be stored under UPROPERTY or they will be incorrectly garbage collected. Another problem is your T Value and SomeObj. If it is not initialized it leads to undefined behavior when used). I also don’t see why you’d need a pointer to a pointer on MyObj, because you could just initialize “UMyObject* MyObj” as nullptr in the struct on a default constructor, and fill it in with a value whenever you get one:

AMyActor::AMyActor(){
    SomeObj = CreateDefaultSubobject<UMyObject>(TEXT("SomeObj"));
    KeyA = FMyStruct();
    KeyA.MyObj = SomeObj;
    ....
};

You are also dereferencing nullptr or undefined on SetValue:

if (*MyObj)

Well :smile: :wink: yes. Let me know if you need some references to places to further explain what I mentioned.

My purpose of doing this is to invoke SomeObj->DoSomething() anytime one of the Key (KeyA, KeyB, etc) has it’s value changed

Since keys can have different datatypes and I’m trying to avoid having to write lots of set-get functions and invoking DoSomething() from them I thought this would be the simplest & driest approach :slight_smile:

Also yes I think assigning them in the constructor to avoid having to use pointer to pointer is a good idea I’ll use that

But as you stated

“MyObj” on the struct can not be a UPROPERTY …

… UObject properties must be stored under UPROPERTY or they will be incorrectly garbage collected

So is there any solution/workaround to achieve what I’m trying to do?
Also a shot in the dark but should I use TWeakObjectPtr ? Though I’m not completely familiar with how it works

If a UObject Pointer as class property is not stored under UPROPERTY, it will be garbage collected, meaning nulled, at any time, destroying your pointer when you don’t want that to happen.

So if you get an error when you add UPROPERTY because the struct is templated, you only have the option to make a non templated struct and add UPROPERTY then.

TWeakObjectPtr is no solution because that specifically does not prevent destruction of its reference, opposite to what you want.

Weak Pointers in Unreal Engine | Unreal Engine 5.5 Documentation | Epic Developer Community

Please help me understand rootset and garbage collection

Yes but normally you create a struct to group known datatypes together as “dumb” data for a known purpose, like the details of a person (age, name, etc.) to load elsewhere, like into a class “Person”. If you create a struct with no specific purpose even a template won’t be enough to fit all use cases. If the goal is not clear, then the possibilities are simply too many.

In cases you might also just use conversion (float to string, struct to string, and back) instead of templating. This gets more common if you are writing string a lot anyway (say to read / write INI). I found this type of overlap when programming in-game setting controls, as some settings would be float (audio volume), others look like bool or enum, but were processed as string through the central system and also most of the time. I then just made a little conversion utility to get everything to string and back when required.