Blueprint TMap 'Find' not returning values by reference

It seems as if the ‘Find’ node for the newly released TMap container does not return values by reference (Though I believe it is meant to). A quick way to check this is to create a TMap with any key or value type (I used Integer, Integer), and then run the following functions:

http://puu.sh/uhMSa/0965a822ac.png

After doing so, you can check the value of key 0, and it will still be 0, despite incrementing it with the IncrementInt macro. I would imagine it is intended to be returned by reference (For obvious reasons), but if it was not intended that way, I would like to put this in as a feature request.

Hi Jamendxman3,

Thank you for the report. I was able to reproduce this behavior so I’ve entered JIRA UE-42388 and our developers will be investigating further. Follow that link to monitor the status of the report.

I did notice something odd that I mention in the report. Adding a key of 1 instead of 0 will allow the 0 key to be incremented.

Cheers,

TJ

It’s intentional that Find (and array Get) returns a copy - but we would like to improve this. UE-6451 is the highest profile bug that is affected by this core behavior - and I spend a lot of time thinking about it.

The reason for this behavior is that we can’t safely return a reference to data thats owned by a container type. Consider this blueprint:

128197-undefined_behavior_bp.png

My current best idea (and there may be other approaches!) is to augment the VM so that we can safely track these references and throw an exception if later statements attempt to access container memory that was freed by subsequent statements.

It’s very important to us that blueprints not hard crash the UE4 process - but if this behavior is not important to you it’s relatively easy to make these nodes return an unsafe reference. A related codepath is associated with the EX_ArrayGetByRef instruction identifier.

Edit:

We did a little digging and the potentially stale reference above is not possible because the reference output pin results in a re-evaluation of the input expression (meaning in the example above we call NewVar1.Get(0) twice and the second time would simply fail with an out of bounds exception.

This is great news - we’re hoping to provide an array Get node that returns a reference in 4.16. A Map::Find node may also make that binary release.

Edit2: by passing the reference to a function you are able to crash the blueprint runtime. We are back to square one on this front. We want blueprints to be safe, fast, and easy to use. That is a rough guide to our priorities and dominates priorities in this case.

Hi! Did this ever happen? I can’t find a Get node returning a reference; every time I have to use the ‘Set members then add it back to the map at the same key’ paradigm, a little part of me cries…

I am also looking forward to an improvement on this side. because I do not see how I can call an own function has an object stored in an array if all that I get is a copy … the only solution in my eyes is to go through a foreachloop … but that does not seems an effective way to me … would I miss something?

As of 4.16 there is a toggle on Array Get nodes that allow it to return by Ref. The problematic blueprint I described above is actually not a problem because we call the Get function again on subsequent accesses, giving us a chance to ensure that the range is valid.

Just wondering if this change is also going to be done to the Map’s Find function, because it is still very frustrating dealing with structures in maps, and I imagine also a bit expensive. Is it planned for any release?

It’s not currently slotted for a release. I’ll see if we can get this on the schedule.

Any chance it can be included in 4.20 at the latest? Would be very useful for us too for map of structs.

Any update on Map’s get Function being added?

An update would be great! I keep asking myself, is this really necessary? - but this thread indicates that it is.

I actually discovered that it was somewhat easy to segfault the runtime using array values passed by ref. That discovery shifted priorities. Unfortunately, making array access safe is a higher priority than allowing unsafe access to map values.

Adding another echo in the void for this functionality in 2020.

I planned out this awesome data structure with a few layers of nested maps and structs, just to find (get it?(I can’t help myself)) out I can’t update the values inside. The best I could use this for is like a static data container… Which is not very helpful, unfortunately.

It would be really convenient more than anything. It took me hours of debugging to figure out that the issue was my find node passing a copy (thought that’s maybe a bad on my part). As far as I can discern, the map find node doesn’t indicate that it does that. Adding a line of tooltip that says that the found value is a copy will already save a lot of debug time for people like me who intuit otherwise.

I have managed to come up with a workaround which I believe is less expensive than storing values in a temporary variable to be added to the top layer map (a common suggestion I found in other sources on the topic).

I’ve attached pics - how I imagined it would work vs how I made it work.

Replys on answerhub aren’t ‘the void’ :smiley: they go straight to my e-mail. We’re still a little hesitant to add this unsafe behavior ‘out of the box’ but if it’s really critical for a project it’s not too hard to add in a code plugin or similar. Would that be helpful?

though that’s maybe a bad on my part

BP as a language tries to ‘just work’ and our reliance on copys to prevent segfaults it not a ‘just works’ moment :frowning:

As far as I can discern, the map find node doesn’t indicate that it does that.

Any output pin that is circular is a copy, any output pin that is a diamond is a reference.

Looks like you’ve found how it must be done. We haven’t had a huge waive of segfaults with array by-ref access, so it’s likely my hesitancy to add the feature is the wrong call. Still, BPs that can crash keep me up at night :frowning:

Thanks for continuing the discussion.