如何定义一个变体 提取模板参数的子类型

2024-02-25

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

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

和转换是一个简单的类型:

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

状态类是非多态的(也不应该是)。我现在的问题是如何定义另一个变体,该变体能够存储表类型中找到的所有可能状态(最好没有重复)。需要类型以类型安全和非多态的方式存储实际状态。

从上表中,我们有一组独特的条目状态:

entry_states = <start,connecting,connected>

以及一组后续状态:

followup_states = <connecting, connected, failed>

因此生成的变体定义应为:

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

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

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

任何想法表示赞赏。然而,多态性并不是一个有效的解决方案。另外,将当前状态保存在 lambda 中也不是一种选择。

谢谢,最好的!

PS:状态机签名看起来像这样(我没有发布完整的代码,因为它对这个问题没有意义,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::tuple_cat它使用std::index_sequence,并且另外直接来自后者。

作为奖励引入的合并类型列表的代码:

#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>()));

将其应用到您的转换表:

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

在 coliru 上观看直播。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何定义一个变体 提取模板参数的子类型 的相关文章

随机推荐