Adding struct pointers to TMap gives different values from original struct

I have nested structs:

USTRUCT()

struct FActorProperties
{
    GENERATED_BODY()
    
    TMap<FName, FMyProperty*> ActorSlotMap;
        
    FTransform Transform;
};

USTRUCT()
struct FSceneData

{
    GENERATED_BODY()
    
    TMap<int, FActorProperties> ActorPropertiesMap;
};

I’m populating data for a new FActorProperties struct for each scene and adding it to SceneData like this:

    FSceneData SceneData = FSceneData();
    for(int i=0; i< SceneCount; i++)
    {
        FActorProperties ActorProperties;
// Derived struct of FMyProperty and the values for this are generated from a property list 
        FMyFloatProperty* FloatProp = GetStructPointerFromProperty<FMyFloatProperty>(Actor, StructProperty);

        ActorProperties.ActorSlotMap.Add(StructProperty->GetFName(), FloatProp);

        ScenesData.ActorPropertiesMap.Add(i, ActorProperties);
    }

I’m leaving out the logic for getting StructProperty values, basically it is generated from property iterator for the actor.
When I debug this ActorProperties, ActorProperties.ActorSlotMap[0] and ActorProperties.ActorSlotMap[1], I’m seeing correct float values 0 and 5.

Log Output:

Adding float value 0.000000

Adding float value 5.000000

Now if I query all the entries in ScenesData.ActorPropertiesMap[i].ActorSlotMap all the FloatProp values are same

Log Output:

float value: 5.000000

float value: 5.000000

I’m an Unreal c++ noob, so maybe I’m not seeing an obvious issue here.What am I doing wrong here?

Looks correct to me.

edit: Removed comment about using GENERATED_USTRUCT_BODY(). It is wrong as indicated in another comment.

edit: Aren’t you only adding one entry into ActorSlotMap? Why are you saying you logged two values?

There are two scenes, so for each scene (i) I’m creating a new ActorProperties struct and adding it

Not to derail the thread too much but this is bad advice. GENERATED_USTRUCT_BODY and GENERATED_UCLASS_BODY macros are both deprecated.

GENERATED_BODY is the more recent macro and is correct in both contexts.

Looks like GENERATED_USTRUCT_BODY is defined as GENERATED_BODY. I was certain I was getting errors with GENERATED_BODY. Guess I was mistaken.

edit: The UE codebase is littered with GENERATED_USTRUCT_BODY.

Could we see how you’re getting the values that you’re doing the log with?

Oh yeah it is! It’s pretty silly! :smiley:

This is how I’m logging the outputs:

  1. ActorProperties map
for(const TTuple<FName, FMyProperty*>& Slot : ActorProperties.ActorSlotMap)
	{
		if(Slot.Value->GetDerivedStruct() == FMyFloatProperty::StaticStruct())
		{
			FMyFloatProperty* FloatProp= static_cast<FMyFloatProperty*>(Slot.Value);
			UE_LOG(LogClass, Log, TEXT("Adding float value: %f"), FloatProp->Value);
		}
	}
  1. SceneData’s map
for(const TTuple<int, FActorProperties>& Scene: SceneData.ActorPropertiesMap)
	{
		for( const TTuple<FName, FMyProperty*>& Slot : Scene.Value.ActorSlotMap)
		{
			if(Slot.Value->GetDerivedStruct() == FMyFloatProperty::StaticStruct())
		     {
			FMyFloatProperty* FloatProp= static_cast<FMyFloatProperty*>(Slot.Value);
			UE_LOG(LogClass, Log, TEXT("float value: %f"), FloatProp->Value);
		     }
		}
	}

Nothing pops out to me. You seem to only be adding FMyFloatProperty. Could you comment out the line with the if statement “if(Slot.Value->GetDerivedStruct() ==…”? Just to make sure you’re not overwriting the value.

There’s only four ways I can think of this happening.

  1. You’re overwriting the same index.
  2. The value pointer is being overwritten.
  3. The value itself is being overwritten.
  4. Your global logging is being run twice (and you only have one value).

I think we can rule out #1 because you wouldn’t get two values.
#2 seems dubious.

#3 is possible which is why I want you to try removing the if statement checking the type.

But do you think #4 is possible? Can add a LOG statement before you start logging the SceneData’s map to ensure it’s not just running twice? If it was running twice, it likely means your data was overwritten. To me, it looks like it’s run twice from different actors and you’re only storing the last invocation. But that’s just a guess.

So my advice is make sure the number of invocations are correct when setting it and when logging it.

edit: Oh, and add the scene number in your logging.

Thank you for the suggestions. I’ve tried logging the scene index as well, and it does log 0 and 1.
This is how I’m generating the property values:

  • Importing a JSON with list of scenes. Each scene has a list of Actors that has list of properties
  • For each actor I’m spawning an actor instance, setting properties from the json. For each scene, I’m overwriting the properties on the actor instance if it’s already spawned.
  • For each actor, the ActorProperties list is generated from the JSON as well, from the output logs everything looks correct
  • Then I’m adding the ActorProperties list to SceneData for each scene. This is where something weird is happening. I’m wondering if FMyProperty pointer is pointing to the actor instance’s property which is overwritten. If that’s the case, then why does the ActorProperties log different values?

ActorProperties.Transform seems to be stored correctly. I can’t store FMyProperty by value because I need to downcast to derived struct when retrieving the values. FloatProperty is just an example, there are other types of properties

This is not a personal project, that’s why I’m unable to share the entire code.

In Visual Studio, you can set a breakpoint when you add the first value. With the debugger paused at that breakpoint, add a DATA breakpoint to your Value in FloatProp->Value. I think there’s a checkbox to stop the debugger when the value changes.

That will let you rule out anything overwriting the Value itself.

Before that though, I would put a LOG statement above this line:
FSceneData SceneData = FSceneData();

If that line runs more than once, you have an invocation issues where it’s being called too often. All of the SceneData needs to be added in this one call because you’re creating the instance here.

You could also print the pointer value of FMyFloatProprty* FloatProp in your logging code. This is to ensure you have different pointers stored.

Aside from that, this looks like you’ll need to do some debugging. The code itself looks fine to me, but I don’t know what is calling it. That’s where I think the problem lies… again just a guess. Maybe someone will see something I’m missing.

I’d also add a LOG statement before your logging with SceneData.Num().

Are you 1000% sure this returns a different pointer? Yes, you logged a different value, but LOG the pointer address too. I have no idea how that value is stored in the property. If by some design, that pointer is meant to be temporary, then perhaps you were meant to copy it (or create a new instance and copy the data).

Just throwing things out there. :slight_smile:

It is the same pointer address :smiling_face_with_tear: I ended up debugging the GetStructPointerFromProperty function.

const void* ValuePtr = StructProperty->ContainerPtrToValuePtr<void>(Owner);

This is why it’s giving us the same pointer, because even though I want different property values, it’s the same actor.
I’m not sure how to fix this though.

How are the values different when you insert it though? Is GetStructPointerFromProperty() setting it?

I’m not familiar with that method (ContainerPtrToValuePtr), but I’ve been reading up on it. I think ContainerPtrToValuePtr returns the Class’ pointer to that property. To get the actual value pointer for your actor, you need to call GetObjectPropertyValue(ValuePtr);

But I still don’t understand how you’re only iterating over one property and expecting two values.

I found this thread. Maybe it’ll help.

1 Like

thanks! GetObjectPropertyValue only works on UObjects and not structs. So
my solution was to copy the value to a new pointer using malloc and then memcpy and preserve the values like that.

this seems to fix my bug. :slight_smile:

1 Like

Ok, make sure to track your memory usage and delete them when done. TSharedPtr should work if you use FMemory::Malloc.

Hope everything works out :slight_smile:

1 Like

I was logging as I was adding the pointers to the list. Between each log, the properties on the actor gets overwritten, so it was giving me the latest value on the property.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.