如何定义变体<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;
};

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

根据以上table,我们有一组独特的进入状态:

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>

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

如有任何想法,我们将不胜感激。但是,多态性不是有效的解决方案。在 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;
};

this answer

中汲取灵感
// ===================================================
// 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...>;
};

有两个单独的任务:

  1. 正在从 transition-table 中提取状态。使用 pattern-matching.
  2. 很容易做到这一点
  3. 正在删除重复项。这可以用 O(log n) 深度来完成,复杂度来自 std::tuple_cat,它使用 std::index_sequence,另外直接来自后者。

合并代码 type-lists 作为奖励抛出:

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

将它应用到您的 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>;

在 coliru 上观看直播。