如何定义变体< x,y,z>提取模板参数的子类型 [英] How to define a variant<x,y,z> extracting subtypes of a template parameter

查看:92
本文介绍了如何定义变体< x,y,z>提取模板参数的子类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建状态机,其中状态转换被描述为变体,即:

I am building a state-machine where state transitions are described as a variant, i.e.:

using table = std::variant<
/*             state        event               followup-state    */
    transition<start,       success<sock>,      connecting>,
    transition<start,       exception,          failed>,
    transition<connecting,  success<>,          connected>,
    transition<connecting,  exception,          failed>,
    transition<connected,   exception,          failed>
    >;

并且转换是一种简单的类型:

and transition being a simple type:

template <typename ENTRY_STATE, typename EVENT, typename NEXT_STATE>
struct transition {
    using entry_state = ENTRY_STATE;
    using event = EVENT;
    using next_state = NEXT_STATE;
};

状态类是非多态的(并且不应是).现在我的问题是如何定义另一个变量,该变量能够存储表类型中的所有可能状态(最好没有重复的状态).需要使用类型来以类型安全且非多态的方式存储实际状态.

The state classes are non-polymorphic (and shall not be). My question now is how to define another variant that is able to store all possible states found in the table type (best without duplicates). The type is needed to store the actual state in a type-safe and non-polymorphic way.

在上表中,我们具有一组唯一的输入状态:

From the above table, we have a unique set of entry states:

entry_states = <start,connecting,connected>

和一组后续状态:

followup_states = <connecting, connected, failed>

因此,最终的变体定义应为:

so the resulting variant definition shall be a:

using states = std::variant<entry_states JOINT followup_states>;

=>  using states = std::variant<start,connecting,connected, failed>

我知道如何从表中提取类型信息并访问特定转换的类型信息,但不知道如何将可能的状态转换为变量定义(没有重复的类型).

I know how to extract type information from the table and access type information of a particular transition, but have no idea how to transfer possible states into a variant definition (without duplicate types).

任何想法都值得赞赏.但是,多态不是有效的解决方案.也不能将当前状态保存在lambda中.

Any idea is appreciated. However, polymorphism is not a valid solution. Also saving the current state inside a lambda is not an option.

谢谢,最好!

PS:状态机签名看起来像这样(我没有发布完整的代码,因为它对问题没有意义,imo):

PS: the state-machine signature looks like that (I am not posting the full code since it is not meaningful for the question, imo):

template <typename TransitionTable, typename Context>
class state_machine {
public:
    template <typename State, typename Event>
    auto push(State & state, Event & event) {
    ...
    }
protected:
    *using states = std::variant<???>;*
    states current_state;
};

推荐答案

有两个单独的任务:

  1. 从转换表中提取状态.模式匹配很容易做到这一点.
  2. 删除重复项.可以使用O(log n)深度来完成,复杂度来自使用std::index_sequencestd::tuple_cat,另外直接来自后者.
  1. Extracting the states from the transition-table. This is easily done with pattern-matching.
  2. Removing duplicates. This can be done with O(log n) depth, the complexity comes from std::tuple_cat which uses std::index_sequence, and additionally directly from the latter.

用于合并作为奖励抛出的类型列表的代码:

Code for merging type-lists thrown in as a bonus:

#include <tuple>
#include <utility>
#include <type_traits>

namespace detail {
    template <template <class...> class TT, template <class...> class UU, class... Us>
    auto pack(UU<Us...>)
    -> std::tuple<TT<Us>...>;

    template <template <class...> class TT, class... Ts>
    auto unpack(std::tuple<TT<Ts>...>)
    -> TT<Ts...>;

    template <std::size_t N, class T>
    using TET = std::tuple_element_t<N, T>;

    template <std::size_t N, class T, std::size_t... Is>
    auto remove_duplicates_pack_first(T, std::index_sequence<Is...>)
    -> std::conditional_t<(... || (N > Is && std::is_same_v<TET<N, T>, TET<Is, T>>)), std::tuple<>, std::tuple<TET<N, T>>>;

    template <template <class...> class TT, class... Ts, std::size_t... Is>
    auto remove_duplicates(std::tuple<TT<Ts>...> t, std::index_sequence<Is...> is)
    -> decltype(std::tuple_cat(remove_duplicates_pack_first<Is>(t, is)...));

    template <template <class...> class TT, class... Ts>
    auto remove_duplicates(TT<Ts...> t)
    -> decltype(unpack<TT>(remove_duplicates<TT>(pack<TT>(t), std::make_index_sequence<sizeof...(Ts)>())));
}

template <template <class...> class TT, class... Ts>
using merge_t = decltype(detail::unpack<TT>(std::tuple_cat(detail::pack<TT>(std::declval<Ts>())...)));

template <class T>
using remove_duplicates_t = decltype(detail::remove_duplicates(std::declval<T>()));

将其应用于您的过渡表:

Applying it to your transitions-table:

template <template <class...> class TT, class ... Ts>
auto extract_states(TT<Ts...>)
-> TT<typename Ts::entry_state..., typename Ts::next_state...>;

using extracted = decltype(extract_states(std::declval<table>()));
using states = remove_duplicates_t<extracted>;

在大肠杆菌上实时观看.

See it live on coliru.

这篇关于如何定义变体&lt; x,y,z&gt;提取模板参数的子类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆