entt: range based `registry::insert_missing` and `registry::remove_if_exists`
I think I found a legitimate use-case for the two above methods.
When you have relationship components which contain std::vector<entity> it’s often not practical to generate a view which in the first case excludes the existing component, and in the second returns only the existing ones and matches all entities of the vector. Currently you have to iterate and add/remove the components individually.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 34 (19 by maintainers)
Commits related to this issue
- entity: registry ::remove/::erase entity/range and safe/unsafe versions (close #486) — committed to skypjack/entt by skypjack 3 years ago
Well, the documentation came with the very first version of
EnTTand i wasn’t sure that this behavior wouldn’t change at the time. Nowadays I suspect this won’t change any time soon though! 😄 So, yes, we can make it clear in the documentation, of course. Probably we should open a different issue for that btw.I’ve added a version of
remove_if_exists(renamed toremoveactually) toexperimental. An iterator based version ofinsert_missingis trickier imho and there is no way to make it faster than its counterpart defined in user space. So, I think we can safely close this issue. Thanks for all the comments!I’ll merge the changes to
mastersoon. Ping me if you try them and find any issue in the meantime.EnTTdoesn’t care much if you put a pack or not as the last argument of your lambda. This is an optimization made by the compiler, if any. It sees that you don’t use the parameters and prunes the code that reaches them. I’ve no idea if it can do the same with an iterable object to be honest. In theory, yes. In practice, it’s implementation defined, so dunno.Also note that in both cases empty types aren’t returned. I usually use this kind of components as filters, so, in this case, there are no differences.
My suggestion is: if you care, try and measure. This is probably the best thing you can do. However, it may be that a compiler manages to make this optimization while another one does not. For example, clang does a great job at vectorizing views and groups while msvc has more difficulties because it isn’t (wasn’t?) that great at optimizing templated stuff.
Indeed yes, C++20 will be a game changer for
EnTT. However, it’s far from being usable at the moment, so we have to wait for a while…Yeah, the documentation is scaring but it works exactly as @Kerndog73 reported. Iterators go back-front to allow insertions/deletions during iterations. It goes without saying that it won’t be possible anymore if you iterate things front-back.
Unfortunately, the problem here is the C++ language itself and its standard library. I’d like to return iterators from views the
value_typeof which isstd::tuple<entity, Comp1 &, ..., CompN &>. However, the standard says explicitly that thereferencetype of a forward iterator must be an actual reference and not a proxy object. In theory, this isn’t strictly required for multi-pass guarantee though. Let’s suppose I decided to use a tuple-like object asvalue_type. This would turn the iterator in an input iterator. However, some algorithms like the parallelstd::for_eachrequire their arguments to be forward iterators. Therefore:No, this would restrict the possibilities to use them with the algorithms provided with the standard library.
That said, single component views and groups offer the
raw<T>member function to get raw pointers to a packed arrays of elements. We can get around the limitations above by using this stuff carefully.@sunbubble Do I get right what you want? You want to make a call like
entt::insert_missing< Comp1, Comp2 > (container.begin (), container.end ())and have components assigned only to those entities from thecontainerthat don’t already have them?