move vs copy when emplacing a context variable #866
Replies: 5 comments
-
A friend has found what seems to cause this change in behavior. One difference between "Development Editor" and "Development" is that the first has exceptions enabled and the other does not. When exceptions are enabled, the emplace will move, and when they are disabled the emplace will copy, causing the bug. In order to fix it, we did this:
With this it seems to move always. Any thoughts on this? |
Beta Was this translation helpful? Give feedback.
-
It does actually. The dense map is a kind of glorified and customized sparse set with a fixed size sparse array. It swaps the elements internally to keep the packed array really packed all the time. So, both movable and copyable objects are fine. Of course, movable types perform way better, as you can guess. Let's look at your callstack. The issue arises when the internal vector runs out of memory and decides to reallocate due to an if constexpr (is_nothrow_move_constructible_v<_Ty> || !is_copy_constructible_v<_Ty>) {
// move
} else {
// copy
} Therefore, everything seems to boil down to |
Beta Was this translation helpful? Give feedback.
-
Yeah, confirmed, #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) As a consequence, the move constructor of if constexpr (is_nothrow_move_constructible_v<_Ty> || !is_copy_constructible_v<_Ty>) { The fallback is then to copy the underlying value. Very tricky. |
Beta Was this translation helpful? Give feedback.
-
Probably, the whole |
Beta Was this translation helpful? Give feedback.
-
In the meantime, I've created an issue here out of this discussion. To be discussed further though. |
Beta Was this translation helpful? Give feedback.
-
Hi,
I have a really weird bug that I'd like to share, in case it rings any bell to someone. I'll start by saying that I'm trying to use entt in an unreal project, so I have targets "Development Editor" and "Development".
I just started using context variables as a way to store singleton state data. Everything was working ok in "Development Editor", but then switching to "Development" it crashes.
I've spent some time debugging, and I noticed that the reason for the crash is that when emplacing a context variable it reallocates a vector, and for some reason it's deciding to copy a previously emplaced context variable instead of moving it. The problem is, this context variable is not movable at all because it has a unique_ptr inside.
To me what's really surprising is having a different behavior in those two targets, also considering that this is all done at compile time.
Looking at the callstack when things go well and things go wrong, the difference is that in the good one std::_Uninitialized_move will be called, and on the bad one std::_Uninitialized_copy. Down the line, in the bad case it will reach basic_any::basic_vtable with the copy operation which will do nothing, because it's compiled out with the if constexpr (so this part detects is_copy_constructible_v fine)
These are the callstacks for the good and bad case, same code but changing from “Development Editor” to “Development”
GOOD
BAD
I’ve tried reproducing the same thing in a small standalone project in case I could post it but I was not able to reproduce it. I guess the issue must be something very specific to the way the projects are compiled.
One thing I don’t understand is how would get_or_emplace on the dense_map know if the type is copyable or not, because all it knows is entt::basic_any<0,8>. It doesn’t explain why in one target it chooses move and in the other copy, but could it be that dense_map just doesn’t support non-copyable types?
Any ideas?
Thank you!
Beta Was this translation helpful? Give feedback.
All reactions