简体   繁体   English

在子类实现中解包元组以调用以可变参数 arguments 为模板的 function (C++)

[英]Unpacking a tuple to call a function templated with variadic arguments in a subclass implementation (C++)

I am in the midst of implementing an Entity Component System .我正在实施Entity Component System I am running into issues when attempting to call a function templated with variadic arguments:尝试调用以可变参数 arguments 为模板的 function 时遇到问题:

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 . engine_system_base负责为Ts中的每种类型注册T On update each system implementation is supposed to retrieve all necessary components from the registry.update时,每个系统实现都应该从注册表中检索所有必要的组件。 I am unfortunately not sure how I can unpack the component_types instance to correctly call a subclass implementation.不幸的是,我不确定如何解压缩component_types实例以正确调用子类实现。

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.组件存储 state 并且没有任何行为。 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_base为子类实现注册T engine_system uses engine_system_base as a variadic base to register each T in Ts with the registry (omitted). engine_system使用engine_system_base作为可变参数基,将Ts中的每个T注册到注册表中(省略)。 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 .然后可以使用move_system翻译所有实体,这些实体由transform_componentrigid_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.在我的第一个实现中,我为每个系统实现单独定义了void update(float delta_time) const ,它可以工作但重复相同的确切实现,只是通过为每个子类显式定义Ts...而有所不同。 Unfortunately I am running into aforementioned issue when attempting to refactor this logic into engine_system .不幸的是,我在尝试将此逻辑重构为engine_system时遇到了上述问题。

Replace ?????????? with std::get<Ts>(c)... assuming that each T in Ts is unique.使用std::get<Ts>(c)...假设Ts中的每个T都是唯一的。

In case you have non-unique Ts , as mentioned in comments, you can use a std::index_sequence :如果您有非唯一的Ts ,如评论中所述,您可以使用std::index_sequence

[&]<auto... Is>(std::index_sequence<Is...>) {
  process_values(delta_time, std::get<Is>(c)...);
}(std::index_sequence_for<Ts...>{});

Assuming component_view type is component_types , std::apply might help:假设component_view类型是component_typesstd::apply可能会有所帮助:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM