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);
}
}