I am in the midst of implementing an Entity Component System. I am running into issues when attempting to call a function templated with variadic arguments:
template <typename... Ts>
struct engine_system : engine_system_base<Ts>... {
using component_types = std::tuple<Ts...>;
// subclass implements this
virtual void process_values(float delta_time, Ts&... ts) const = 0;
void update(float delta_time) const {
auto component_view = registry.get_view<Ts...>();
for (component_types& c : component_view){
process_values(delta_time, ?????); // issue
}
}
};
The engine_system_base takes care of registering T for each type in Ts. On update each system implementation is supposed to retrieve all necessary components from the registry. I am unfortunately not sure how I can unpack the component_types instance to correctly call a subclass implementation.
Here is a full example (registry omitted):
// components are just "plain old data"
struct vec3 {
float x, y, z;
};
struct transform_component {
vec3 position, rotation, scale;
};
struct rigid_body_component {
vec3 velocity, acceleration;
};
Components store state and have no behavior. Systems implement behavior based on Components.
// internal systems
template <typename T>
struct engine_system_base {
engine_system_base() { /* register T for system in registry */ };
virtual ~engine_system_base() = default;
};
template <typename... Ts>
struct engine_system : engine_system_base<Ts>...{
using component_types = std::tuple<Ts...>;
virtual void process_values(float delta_time, Ts&... ts) const = 0;
void update(float delta_time) const {
auto component_view = registry.get_view<Ts...>();
for (auto& c : component_view){
process_values(delta_time, ?????); // issue
}
}
};
engine_system_base registers T for a subclass implementation. engine_system uses engine_system_base as a variadic base to register each T in Ts with the registry (omitted). Afterwards a system can be implemented as such:
struct move_system : engine_system<transform_component, const rigid_body_component> {
void process_values(float delta_time, transform_component& tc, const rigid_body_component& rb) const final {
tc.position = rb.velocity * delta_time;
}
};
The move_system can then be used to translate all entities, which are comprised of a transform_component and rigid_body_component.
int main() {
move_system ms{};
ms.update(0.016);
return 0;
}
In my first implementation I defined void update(float delta_time) const individually for each system implementation, which works but duplicates the same exact implementation and only differs in by explicitly defining Ts... for each subclass. Unfortunately I am running into aforementioned issue when attempting to refactor this logic into engine_system.
CodePudding user response:
Replace ????? with std::get<Ts>(c)... assuming that each T in Ts is unique.
In case you have non-unique Ts, as mentioned in comments, you can use a std::index_sequence:
[&]<auto... Is>(std::index_sequence<Is...>) {
process_values(delta_time, std::get<Is>(c)...);
}(std::index_sequence_for<Ts...>{});
CodePudding user response:
Assuming component_view type is component_types, std::apply might help:
void update(float delta_time) const {
auto component_view = registry.get_view<Ts...>();
for (auto& c : component_view){
std::apply([&](auto&... args){ process_values(delta_time, args...); }, c);
}
}
