Entt: Call for comments: get rid of single instance components (aka tags)

Created on 31 Jan 2018  路  12Comments  路  Source: skypjack/entt

I plan to get rid of single instance components. It is not a final decision yet, but I'm taking it seriously into consideration.

The reason is that they are useful sometimes (usually because of a design flaw in the client indeed), but one can easily work around the lack at application level.
As an example, in case EnTT was allocation-aware, a proper _allocator_ that asserts on the second assignment would be enough. Another solution is that the system aimed to tag the entity is the same that stores the tag and refers it later, other than provide the identifier to those that are interested.

BTW, there is no reason for them to live in the Registry. On the other side, they are source of troubles when it comes to extend EnTT to offer save/restore functionalities and some other features.

What about if I get rid of them? Comments are appreciated.

discussion

All 12 comments

I was using tags because it was convenient for things like getting a CameraComponent without a view.

I should probably be doing this another way though.

Personally, I think getting serialization at the cost of tags is a good trade off.

To be honest I think they are very convenient. I'm building a game / prototype, not a framework so my code is not very extendable by its nature. What would be the alternative? Because when EnTT gets rid of tags and there is no easy migration I would be stuck with the current version.

@mario-deluna
Tags are components at the end of the day.
Consider this:

auto entity = registry.create();
registry.attach<MyTag>(entity);

You can access it later as:

if(registry.has<MyTag>()) {
    auto entity = registry.attachee<MyTag>();
    auto &tag = registry.get<MyTag>();
    // :..

So far, so good. Let's see how you would do that with components:

auto entity = registry.create();
registry.assign<MyTag>(entity);

Almost the same indeed. Iteration becomes:

registry.view<MyTag>().each([](auto entity, auto &tag) {
    // ...
});

The sole difference is that they are no longer guaranteed to be _single instance components_. But you can easily use a component in place of a tag.
Am I missing something? Do you see any problem?

So at the core tags are nothing more than components, so there would be also no performance loss?
Would it be a bad idea to then implement a helper like firstAttachee which directly returns the entity identifier? That would allow keeping the migration very simple without having to wrap everything in an each iteration.

They are not exactly the same thing under the hood, but the change won't introduce any performance hit.

Would it be a bad idea to then implement a helper like firstAttachee which directly returns the entity identifier?

Actually, we can probably keep the same interface and map it on a sparse set as we do for the components.
Custom allocators then will force the requirement for a _single instance_ sooner or later.

@mario-deluna I confirm you that we can keep intact the API and map everything to the same data structures under the hood.
The sole penalty in which we can incur is in terms of memory usage, because this way vectors are used also for tags. How vectors allocate is implementation defined, bit you can control it somehow through a call to reserve.
An allocator-aware EnTT will solve the problem sooner or later with a runtime assertion probably.

not a big deal for me if you want to deprecate them or introduce some small performance hit on tags

Damn guys, all of you use tags, really? I thought it was only a secondary, pretty unused feature!!


Ok, so, the other way around is to find a good idea to serialize them. :-D
Anyone that comes up with a solution?

The problem is that I'd like to have an API like this to serialize things:

template<typename... Comp>
void save(...);

However it doesn't work because tags aren't considered at all.
What would work is something like this:

template<typename... Comp, typename... Tag>
void save(ListType<Comp...>, ListType<Tag...>, ...);

Where ListType is a fake class the aim of which is only to (uhu) _list types_.
Pretty ugly, isn't it? Any good idea here? :-)

Hi! So far I've only played with EnTT. So my opinion must weigh less. Besides I know that all of you are better programmers than me. However, I have been asked for opinion and I will give my humble opinion: From what I can understand;

a) Deprecating/removing the feature does not affect the performance. True?

b) Deprecating/removing the feature provides a lighter and simpler design, it reduces the amount of code to maintain, it reduces the documentation...

c) Deprecating/removing the feature is convenient for new and powerful features (with lighter and simpler design) [serializationn and probably others to come]

d) There are alternatives for the feature. In fact, deprecating/removing the feature can favor better designs (!!). Being a convenient feature does not necessarily make it good.

e) Apparently, The only reason to keep this feature is to avoid breaking changes in the API. True?

I think that big features (serialization, Save/Restore, ...) imply a major version change. And therefore, compatibility with previous versions can not be demanded or expected..

DJuego

P.S: Sorry but i disagree with "we can keep intact the API and map everything to the same data structures under the hood." I think it's not a good design decision. :-|

Only ,my two cents

@DJuego

a) Well, removing features doesn't affect performance of other features usually, right.

b) Yes and no. I mean, tags are managed with their own dedicated data structures because they allow types with data members. This introduced a bit of complexity, of course. By getting rid of them or allowing only empty types would greatly simplify things.

c) Serialization for sure, but it will be easier also to introduce custom allocators.

d) My point of view. I'm not necessarily right with this. :-)

e) Yes and no. It's a breaking change and probably we are removing an useful feature. That's why I'm asking for comments!! ;-)

Of course, compatibility cannot be demanded or expected. On the other side, I've no reason to ignore comments from the users of EnTT.
You are helping me to make EnTT a better piece of code every day, so feel free to tell me that I'm doing something wrong if it's so!! ;-)

I also agree with your PS actually. That's why I didn't push anything yet.

Here is an interesting topic on SO about this _problem_.

I'm closing the issue for it's clear that single instance components are a desiderata.
I'm doing my best to keep them onboard.

Was this page helpful?
0 / 5 - 0 ratings