entt icon indicating copy to clipboard operation
entt copied to clipboard

basic_sparse_set - assure_at_least Assertion `elem == null' failed.

Open am-software-solutions opened this issue 1 year ago • 11 comments

Hello,

how can this assertion happen?

The call looks like: reg.emplace_or_replace<Component>(entity, SOME_STATIC_STRING); Which leads to the assert on: image

The component to add looks like:

struct Component
{
    QString str;
};

A check with reg.valid on the entity did not return any error (and a couple of time the call works without causing the assert). Not sure what else to check, as far as I can tell, the entity was also not deleted...

entt version:

#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 11
#define ENTT_VERSION_PATCH 0

Thanks in advance.

am-software-solutions avatar Dec 11 '23 15:12 am-software-solutions

Do you have a repro? At first glance, it looks like you're assigning the same component to the same entity twice.

skypjack avatar Dec 11 '23 16:12 skypjack

Unfortunately not, but would that cause any problems? I thought emplace_or_replace would ignore the case when there is a component already present...

am-software-solutions avatar Dec 11 '23 16:12 am-software-solutions

        if(!reg.try_get<Component>(ent))
        {
            reg.emplace_or_replace<int>(ent, 123);
            reg.emplace_or_replace<Component>(ent, SOME_STATIC_STRING);
        }

ok, really strange, that crashes still on the Component emplace call, so now I'm out of ideas...

am-software-solutions avatar Dec 11 '23 16:12 am-software-solutions

It can be anything. A multi-threading issue, a call on a moved-from registry, whatever. I can't help much without a repro, really.

skypjack avatar Dec 11 '23 17:12 skypjack

Hmm, I already checked that, only one thread calls the code and there is no move done (singleton registry)... Is there any reason why the assert is not in this code: https://github.com/skypjack/entt/blob/f2c417435c021b6bd2e426ffd9d1fc116b804146/src/entt/entity/sparse_set.hpp#L189 But in here: https://raw.githubusercontent.com/skypjack/entt/f2c417435c021b6bd2e426ffd9d1fc116b804146/single_include/entt/entt.hpp Without knowing the internal structure in detail, its hard to understand what this assert is causing, to me the code looks like the assert should actually not happen. Can maybe someone explain in what condition this assert is hit? I guess that would help to check if/how the calling code messes something up.

am-software-solutions avatar Dec 12 '23 06:12 am-software-solutions

The assert tells you that the slot is already taken. It means that the set already contains the entity. That's all. The component type doesn't really matter too, it would be the same for a storage<void> type. Without a repro it's nearly impossible to tell why you're hitting this though. I'm sorry. You're the only one that can debug it. I agree that the assert should not happen. It's there to save all users (including me) from their own mistakes. 🙂

skypjack avatar Dec 12 '23 08:12 skypjack

Are shared libraries involved?

Innokentiy-Alaytsev avatar Dec 12 '23 08:12 Innokentiy-Alaytsev

The assert tells you that the slot is already taken. It means that the set already contains the entity. That's all. The component type doesn't really matter too, it would be the same for a storage<void> type. Without a repro it's nearly impossible to tell why you're hitting this though. I'm sorry. You're the only one that can debug it. I agree that the assert should not happen. It's there to save all users (including me) from their own mistakes. 🙂

Hmm, strange that this (entity already in the set) matters on a call of emplace_or_replace (-> if so, do a replace?), but there will be reasons I guess XD

Are shared libraries involved?

Yes. Maybe smth leaks into the registry, that the is the last thing I can think of. Thanks.

am-software-solutions avatar Dec 12 '23 09:12 am-software-solutions

Try looking into EnTT documentation about working with shared libraries. If you don't do things properly, then each shared library will have it's own set of identifiers for components and stuff.

Innokentiy-Alaytsev avatar Dec 12 '23 09:12 Innokentiy-Alaytsev

Try looking into EnTT documentation about working with shared libraries. If you don't do things properly, then each shared library will have it's own set of identifiers for components and stuff.

Thanks for pointing it out, do you mean in the wiki? Could you please provide a link to what you are referring to?

am-software-solutions avatar Dec 12 '23 10:12 am-software-solutions

https://github.com/skypjack/entt/wiki/Push-EnTT-across-boundaries

Innokentiy-Alaytsev avatar Dec 12 '23 10:12 Innokentiy-Alaytsev

Just FYI, I had the exact same assertion when emplacing a component after previously accessing the same type of component from a deleted entity (Which of course is wrong).

Something like this

reg.emplace<Transform>(e, {});
reg.destroy(e);
reg.get_or_emplace<Transform>(e); // bad

// ...

auto e2 = reg.create();
reg.get_or_emplace<Transform>(e2); // triggers assert

hfn92 avatar Jan 11 '24 15:01 hfn92

Just FYI, I had the exact same assertion when emplacing a component after previously accessing the same type of component from a deleted entity (Which of course is wrong).

Something like this

reg.emplace<Transform>(e, {});
reg.destroy(e);
reg.get_or_emplace<Transform>(e); // bad

// ...

auto e2 = reg.create();
reg.get_or_emplace<Transform>(e2); // triggers assert

N1. That looks like the issue 👍🏻😀

am-software-solutions avatar Jan 11 '24 18:01 am-software-solutions

Wait a moment. In this case, EnTT should throw an assert to you. Did you disable them?

skypjack avatar Jan 12 '24 11:01 skypjack

Wait a moment. In this case, EnTT should throw an assert to you. Did you disable them?

No I did not. I was confused about that as well.

EDIT: Can you try this?

  entt::registry reg;
  auto e = reg.create();
  reg.emplace<int>(e);
  reg.destroy(e);
  reg.get_or_emplace<int>(e); // no assert

  auto e2 = reg.create();
  reg.emplace<int>(e2); // assert

hfn92 avatar Jan 12 '24 11:01 hfn92

Oh, I see. The set doesn't contain the entity and therefore you can emplace the element. However, the second emplace finds the slot occupied and triggers an error. Makes sense. In theory, I can add a valid(entt) check within the get_or_emplace function but nothing prevents you from accessing the storage as in .storage<int>() and getting around the check if you like.

skypjack avatar Jan 12 '24 17:01 skypjack

Well an assertion at the point of error would have saved me some time in this case. I think it's worth adding even if it doesn't work when accessing the storage directly.

hfn92 avatar Jan 17 '24 13:01 hfn92

It's your baby :), you can judge best, but asserting asap sounds good to me.

am-software-solutions avatar Jan 17 '24 17:01 am-software-solutions

Mmm, ok, let's reopen the issue as feature request then. We can add the checks back in place with the next release. 👍

skypjack avatar Jan 19 '24 14:01 skypjack

Checks available on the wip branch (with non-regression tests too). They'll be part of the next release. Thanks for pointing this out. 👍

skypjack avatar Feb 02 '24 09:02 skypjack