繁体   English   中英

如何定义变量<x,y,z>提取模板参数的子类型

[英]How to define a variant<x,y,z> extracting subtypes of a template parameter

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

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上看到它。

这个答案中获取灵感

// ===================================================
// is_in < type, variant<...> >
//     is true_type if type is in the variant
//     is false_type if type is not in the variant

// Assume TElement is not in the list unless proven otherwise
template < typename TElement, typename TList >
struct is_in : public std::false_type {};

// If it matches the first type, it is definitely in the list
template < typename TElement, typename... TTail >
struct is_in < TElement, std::variant< TElement, TTail... > > : public std::true_type {};

// If it is not the first element, check the remaining list
template < typename TElement, typename THead, typename... TTail >
struct is_in < TElement, std::variant< THead, TTail... > > : public is_in < TElement, std::variant< TTail... > > {};

// ===================================================
// add_unique < TNew, typelist<...> >::type
//     is typelist < TNew, ... > if TNew is not already in the list
//     is typelist <...> otherwise

// Append a type to a type_list unless it already exists
template < typename TNew, typename TList,
  bool is_duplicate = is_in < TNew, TList >::value
  >
struct add_unique;

template < typename TNew, typename TList,
  bool is_duplicate = is_in < TNew, TList >::value
  >
using add_unique_t = typename add_unique<TNew, TList, is_duplicate>::type;

// If TNew is in the list, return the list unmodified
template < typename TNew, typename... TList >
struct add_unique < TNew, std::variant< TList... >, true >
{
  using type = std::variant< TList... >;
};

// If TNew is not in the list, append it
template < typename TNew, typename... TList >
struct add_unique < TNew, std::variant< TList... >, false >
{
  using type = std::variant< TNew, TList... >;
};

// ===================================================
// process_arguments < Args... >::type
//     returns a variant of types to be inherited from.
//
// It performs the following actions:
// a) Unpack variant <...> arguments
// b) Ignore values that are already in the list

template < typename... Args >
struct process_arguments;

template < typename... Args >
using process_arguments_t = typename process_arguments<Args...>::type;

// Unpack a variant in the first argument
template < typename... VArgs, typename... Args >
struct process_arguments < std::variant < VArgs... >, Args... >
{
  using type = process_arguments_t < VArgs..., Args... >;
};

// End the recursion if the list is empty
template < >
struct process_arguments < >
{
  using type = std::variant<>;
};

// Construct the list of unique types by appending them one by one
template < typename THead, typename... TTail >
struct process_arguments < THead, TTail... >
{
  using type = add_unique_t < THead, process_arguments_t < TTail... > >;
};

然后我们可以将process_arguments应用于TransitionTable的参数

template<typename Table>
struct transition_traits;

template<typename... Transitions>
struct transition_traits<std::variant<Transitions...>>
{
  using entry_states = process_arguments_t <typename Transitions::entry_state...>;
  using next_states = process_arguments_t <typename Transitions::next_state...>;
  using states = process_arguments_t <typename Transitions::entry_state..., typename Transitions::next_state...>;
};

暂无
暂无

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

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