-
-
Notifications
You must be signed in to change notification settings - Fork 904
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Duplicate storage for the same component #1063
Comments
It would be the first case of conflict in many years but anything is possible. How is |
using Children = std::vector<entt::entity>;
You mean like this? namespace entt
{
template<>
struct type_hash<::Children, void> final
{
[[nodiscard]] static constexpr id_type value() noexcept
{
return ~0;
}
[[nodiscard]] constexpr operator id_type() const noexcept
{
return value();
}
};
} // namespace entt This solve the issue for me. Hash is forced to
|
Wait a moment. |
I know. I just wanted to make sure EDIT: Also my problem is that I get multiple storages for the same type ( |
A repro would help but 🤷♂️ no idea what you're doing that can cause this, I'm sorry. |
This looks like a nasty bug somewhere When I print the storage at my first usage I get:
When I then later print all storages I get:
It looks like the hash of the initial storage at I'll try to get minimal repro but I don't have a lot of hope 😆 EDIT: This info is wrong. The storage already existed before that. Created by |
Two new things.
template <typename Type>
[[nodiscard]] auto &
assure([[maybe_unused]] const id_type id = type_hash<Type>::value()) {
if constexpr (std::is_same_v<Type, entity_type>) {
return entities;
} else {
static_assert(std::is_same_v<Type, std::decay_t<Type>>,
"Non-decayed types not allowed");
auto &cpool = pools[id];
if (!cpool) {
using storage_type = storage_for_type<Type>;
using alloc_type = typename storage_type::allocator_type;
auto op = type_id<Type>();
printf("Type=%s \ntype_id<Type>().hash()=%u \nid=%u "
"\ntype_hash<Type>::value()=%u\n",
op.name().data(), op.hash(), id, type_hash<Type>::value());
if constexpr (std::is_same_v<Type, void> &&
!std::is_constructible_v<alloc_type, allocator_type>) {
And for
The id that is passed as default value is inconsistent with those computed in the method. While for other types it"s consistent
Any ideas? Is this a compiler issue? |
To be honest, it really really really smells of UB. As if you were a moved-from registry or a dangling reference. Sort of. |
Yes I'm definitely using the same registry (you can see the same pointers for the registry in the first post). I'm also adding thousands of components in the same function, but only Also I don't see how UB would cause
Sorry I wish I had one 😭 But all I'm doing is reg.emplace<std::vector<entt::entity>>(e) with different random result.
No sorry, I realize this is difficult to remote diagnose. Still I really appreciate your help 👍 |
Btw if I change using Children = llvm::SmallVector<entt::entity, 4>; I also have no issues ... I guess the hasher does not like |
Maybe the using declaration gives it a headache? I guess you tried to reproduce it like this, right? |
I personally would not use |
I don't want to create a new type. I was just using a typedef for convenience. The issue persists regardless of using the typedef or |
I have a repro, but I almost certain you won't get the same result 😞 This is my output. It shows two storages with the same type but different hash. (The hash of the type in storage is different from the has used as map key)
Minor unrelated changes such as this already fix this issue ...
|
Ehy, sorry for the late reply, I've been busy last week. Gonna try it soon. 👍 |
I can't reproduce your result with this code. To be honest, I don't even see where |
Yeah I didn't think it would work for you 😅
The class is not used. The code is utter nonsense since I just removed code from my engine just until before the error would stop occurring. |
I have confirmed that the compiler uses two different strings to compute the hash during compile time.
With hashes
I have no idea what to do with that information 🤣 |
I mean, have you tried with a different compiler too? Like, dunno, clang or even gcc 14? |
I want to chime in here, because I'm getting some very similar behaviour as well. I have a project that was running perfectly using entt 3.6, but recently updated it to 3.12 and I noticed one of my unit tests was failing. But it would only fail when running in the Continuious Integration system we are running. I too tracked problem down to having two storage locations for the same type. When creating the object it would create one storage, then when looking it up, it would search in another storage location. My component is also a I got the same error using gcc8 and gcc11. But ONLY when they are running inside a docker container. When I compile the same unit tests on my main system (which use gcc11), all the tests pass and I have no problems. I tried the suggested solution of implementing a custom I also tried not using std::vector as a component, but instead defined a wrapper struct around the vector like so: template<typename T>
struct VectorComponent
{
std::vector<T> _vector;
} This also seemed to work. So the problem may be due to how std::vectors are being type_hashed. It may be a compiler bug. I will try to compile using clang and see if I get the same issue |
To be clear, the type hash relies on a string, which is implementation defined behavoir (/ compiler specific extension?), and as such is just best effort. But it rarely breaks. eg: I had to define the hashes myself, because I went across the compiler boundary. @GavinNL if you just want it to work and are ok with extra types (like the |
Thanks for the info @Green-Sky . I was going to inherit from the vector, but I heard it's bad practice to inherit from std containers. I was only using that component in one place, so it was pretty easy to fix. I just did some testing with the hashs here's what happened on my tests.
The type_hash is used to basically get a number so that it can be used as a key for a map, correct? Is there a reason std::type_index wasn't used instead? constexpr reasons, maybe? |
This is interesting though. We have these compilers on the CI, so in theory we can reproduce the issue with a minimal repro? |
Hi @skypjack , I will try to get you a minimal code that reproduces this error |
Not sure if this helps to fix it but it would be great if we had something that fails on the CI because I cannot reproduce the error locally and I don't have gcc11 up and running locally, I'm sorry. 🙏 |
I'll do my best. The main problem was using It was a really odd issue because it wasn't very inconsistent in when it would fail. Only my unit tests were failing, my actual application was running fine. I had the Its likely the "string" that's being generated by gcc that is used for computing the hash for std::vector was not being generated the same way in different parts of the code. |
I just tested @hfn92 example code on GCC11 and I'm getting the same errors he is. I also tested in Clang 14 debug/release, they both seem to be okay I can confirm that the error is happening on @skypjack ,Is your CI building in debug mode or release mode? |
Here's @hfn92 test, with some cmake and conan files. This definitely fails on my end My system is: Linux Mint 21/Ubuntu 22.04, GCC11.4 + release build |
Thank you a lot. That's really funny btw. I wonder what changes in release mode on the gcc side. |
I just did some additional testing. In the code I gave you, there are two calls to std::cout << "Calling emplace<Children>()" << std::endl;
constexpr auto stripped = entt::internal::stripped_type_name<Children>();
constexpr auto value = entt::hashed_string::value(stripped.data(), stripped.size());
std::cout << " Stripped name :" << stripped << std::endl;
std::cout << " hashed name :" << value << std::endl; This is the output I got when running with GCC11.4+Release+Ubuntu22.04
|
Could it be that at some point in the code the compiler is unable to see the whole type declaration, making it skip the |
Some more poking around... In If I comment out the static member function, I get the following, which seems like it's working. Note that the name does not contain the allocator part.
I don't know which one should be considered the correct name, In release mode, I think if it can detect a function isn't being used, it doesn't include it in the compilation, but why that would change the name of the compile-time string, I don't know. It might be worth asking a GCC developer. I don't know if this is a bug or not |
I have, just no response yet :) entt uses
I remember getting a change in behavior from completely unrelated changes. Like here #1063 (comment) I think |
I find this one fascinating but I don't see what EnTT can do here. It really looks like an issue on the compiler side. I'm open to suggestions though. If you think we can do more, please drop a few lines to describe your idea. |
When defining Would it be reasonable to provide a way to switch to the counting method for hashes while still retaining the name? In this case I don't mind that the string might not be "correct", since I just use it for display purposes |
Well, you can specialize |
We have a solution and I'm fine with "wont fix:, but I think we should at least have this issue searchable in case other people have have come across the same issue. |
Fair enough but how do we make the issue searchable? I mean, it's so already and it contains lot of words that match with the right keywords. How can we improve this eventually? |
Instead of setting this issue as closed/wont fix, is there a way to mark it as "Known Issue" ? If not, maybe we can put it in the documentation under a section called "Known Issues"? We can explain the error along with the solution. |
I don't think of a way to do that in GH but EnTT has a FAQ doc that fits the purpose maybe? |
Sure, that will work :) |
I've been running into the problem that some components went "missing". After investigating I noticed at some point a second storage for the same type is being created.
I used following code for outputting info (trying with different namespaces and without typedef)
I was able to get two different outputs right after creating the entity
There now seems be be a new storage for the type
Children
.When printing out all storages with
I get following output (:exclamation: this is from a different run so the pointers are different from above):
Things to note
I suspect it has something to do with hashes being created differently, since in the output above only one of the strorages hash matches. But I'm a bit at a loss here on how to proceed 😅
The text was updated successfully, but these errors were encountered: