Entt: Ability to signal entt::collector.replace without doing actual "full" replace

Created on 4 Feb 2020  路  13Comments  路  Source: skypjack/entt

Maybe we need some function that signals replace without doing actual "full replace" ?
For example, I have changed a one small member of my component, and want to signal "on_replace" for already existing observer.

triage

Most helpful comment

I'm to push some changes to address this issue. To sum up, replace has now this form:

registry.replace<T>(entity, [](T &instance) { /* ... /* });

However, a _no-callback_ form is supported as well:

registry.replace<T>(entity);

This does nothing but trigger the associated event. It should be suitable for uses in combination with views and groups, as mentioned by @Kerndog73 above:

void update(entt::registry &reg) {
    auto view = reg.view<BigFella>();

    for(entt::entity e: view) {
        view.get(e).arr[5] = 42;
        reg.replace<BigFella>(e);
    }
}

Maybe not the easiest form to read but that's it, it came as a side-effect to be honest. :smile:
The only _constraint_ is that the replace function is a non-const one. It shouldn't be a problem anyway, since if you're editing a component it's likely that you've a non-const registry at hand, otherwise you couldn't create a view or a group for a non-const type.

All 13 comments

I mentioned this aaaaaaages ago when entt::observer was introduced.

What's the expected API though? I can see only two ways to do that:

  • Require users to trigger explicitly the signal like:

    registry.get<T>(entity).data = value;
    registry.trigger_on_change<T>();
    
  • Add a constrained overload for get (or another member function in case of ambiguities) like:

    registry.get<T>(entity, [](auto &&instance) { instance.data = value; });
    

    Note that none of them can work with views and groups. We must go necessarily through a registry to trigger the signals. It means that we cannot have the same overload in the API of a view or a group, in a similar way to what happens today and for which there is no replace in their API.

Any other idea?

I had something like this in mind:

struct BigFella {
  int arr[100];
};

void update(entt::registry &reg) {
  auto view = reg.view<BigFella>();
  for (entt::entity e : view) {
    view.get(e).arr[5] = 42;
    reg.trigger_on_change<BigFella>(e);
  }
}

Note that none of them can work with views and groups. We must go necessarily through a registry to trigger the signals. It means that we cannot have the same overload in the API of a view or a group, in a similar way to what happens today and for which there is no replace in their API.

yesterday i thought about that too, yeap, unfortunately...

What about range version:

struct BigFella {
  int arr[100];
};

void update(entt::registry &reg) {
  auto view = reg.view<BigFella>();
  for (entt::entity e : view) {
    view.get(e).arr[5] = 42;
    reg.trigger_on_change<BigFella>(e);
  }

  reg.trigger_on_change<BigFella>(view.begin(), view.end());
}
void update(entt::registry &reg) {
  auto view = reg.view<BigFella>();
  for (entt::entity e : view) {
    view.get(e).arr[5] = 42;
    reg.trigger_on_change(view, e); //:D
  }
}

I wouldn't do it in a loop)

I wouldn't do it in a loop)

yeap, maybe you are right!
but for example, sometimes, I'm skipping some entities in the loop..

yesterday i thought about that too, yeap, unfortunately...

It's not a big problem. Nowadays, getting the component from the registry has no more the penalty it used to have and therefore it's comparable to getting it from a view/group.
The problem was the lookup of the pool at the time, so... :)

Btw I see you all like more the _trigger-the-signal-explicitly_ approach?
I would have said the other way around was less error-prone to be honest.

registry.get<T>(entity, [](auto &&instance) { instance.data = value; });

I think a better name for this is something like with.

My only concern is that the name should be consistent with assign_o_replace.
So get isn't a good choice but probably is not with either. Maybe replace is just as good as it can be.

There is a problem with this feature.

Currently we have a signal on replace that passes to the listener the registry, the entity and the new component, while the old instance is available directly from the registry.
This doesn't fit well with this feature because we want to edit the component _in place_. That is, we should make a copy of the current version before to pass a reference to the caller. However, this sets a new constraint: the type must be at least copy constructible. Note that we cannot just _move_ the component before editing it because we don't want to update an object that is in a valid but unspecified state. This could work for basic structures but it won't work in case of slightly more complex types.

While setting the copy-constructible constraint on the type is a viable solution, another one could be to update the signal definition and stop passing it both the instances.
This moves the burden of signalling on the user side when we need to deal with both versions of a component during a replace. However, in my experience I've never did it, so probably this is a good compromise.

Any feedback is appreciated.

I'm to push some changes to address this issue. To sum up, replace has now this form:

registry.replace<T>(entity, [](T &instance) { /* ... /* });

However, a _no-callback_ form is supported as well:

registry.replace<T>(entity);

This does nothing but trigger the associated event. It should be suitable for uses in combination with views and groups, as mentioned by @Kerndog73 above:

void update(entt::registry &reg) {
    auto view = reg.view<BigFella>();

    for(entt::entity e: view) {
        view.get(e).arr[5] = 42;
        reg.replace<BigFella>(e);
    }
}

Maybe not the easiest form to read but that's it, it came as a side-effect to be honest. :smile:
The only _constraint_ is that the replace function is a non-const one. It shouldn't be a problem anyway, since if you're editing a component it's likely that you've a non-const registry at hand, otherwise you couldn't create a view or a group for a non-const type.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Kerndog73 picture Kerndog73  路  5Comments

dBagrat picture dBagrat  路  4Comments

nitishingde picture nitishingde  路  3Comments

Qix- picture Qix-  路  6Comments

skypjack picture skypjack  路  6Comments