Создание шаблона variadi c из другого шаблона variadi c - PullRequest
7 голосов
/ 10 марта 2020

Если честно, я не знаю, как начать искать решение проблемы, которую я пытаюсь решить. Возможно, решение уже есть. Итак, задача здесь.

У меня есть класс, который на самом деле является шаблоном с 2 аргументами:

template <typename F, typename S>
class trans {...};

также у меня есть другой класс, который содержит цепочку этих «транс» классов, таких как tuple (пример):

class holder {
    using chain_type = std::tuple<trans<std::string, int>, 
                                  trans<int, float>, 
                                  trans<float, std::string>, 
                                  trans<std::string, json>>;
};

И, как видно, каждый второй аргумент «trans» совпадает со следующим первым аргументом. Цепочка:

std::string -> int -> float -> std::string -> json.

что бы я хотел ... Я хотел бы иметь какой-нибудь способ сделать эту цепочку такой:

template <typename ...Args>
class holder {
    using chain_type = trans_chain_create_t<Args...>;
};

holder<std::string, int, float, std::string, json> h;

Возможно ли это? Я не очень знаком с шаблонами variadi c и использую их очень редко.

Ответы [ 5 ]

9 голосов
/ 10 марта 2020

Да, это возможно:

template< typename F, typename S >
class trans {};

template< typename F, typename S, typename... Tail >
struct create_trans_chain;

template< typename F, typename S, typename... Tail >
using create_trans_chain_t = typename create_trans_chain< F, S, Tail... >::type;

template< typename F, typename S >
struct create_trans_chain< F, S >
{
    using type = std::tuple< trans< F, S > >;
};

template< typename F, typename S, typename Next, typename... Tail >
struct create_trans_chain< F, S, Next, Tail... >
{
    using type = decltype(std::tuple_cat(
        std::declval< create_trans_chain_t< F, S > >(),
        std::declval< create_trans_chain_t< S, Next, Tail... > >()));
};
6 голосов
/ 10 марта 2020

С Boost.Mp11 , это довольно коротко (как всегда):

template <typename ...Args>
using trans_chain_create_t =
    mp_transform<trans,
        mp_pop_back<std::tuple<Args...>>,
        mp_pop_front<std::tuple<Args...>>>;

mp_transform в основном zip, мы молнии (Args без хвоста) с (Args без головы) и затем попарно применяя trans к этому.

Вы можете разделить вышеперечисленное, добавив вспомогательную метафункцию, zip_tail:

template <template <typename...> class F, typename L>
using zip_tail = mp_transform<F, mp_pop_back<L>, mp_pop_front<L>>;

template <typename ...Args>
using trans_chain_create_t = zip_tail<trans, std::tuple<Args...>>;
3 голосов
/ 10 марта 2020

Просто разверните рекурсивный шаблон с конечной специализацией. Как это работает, описано внутри кода в комментариях. Взгляните:

class json; // as you like that in your given code example... we need to define it
using input = std::tuple< std::string, int, float, std::string, json >;

// First we define a template struct which takes 1 parameter
// No need for a definition as we specialize later
template <typename INPUT >
struct Transform;

// for all inputs which have at minimum 3 template parameters 
// inside the std::tuple parameter we use this specialization 
template <typename FIRST, typename SECOND, typename NEXT, typename ... TAIL >
struct Transform< std::tuple<FIRST, SECOND, NEXT, TAIL...>>
{
    // As we have more than 2 parameters, we continue to transform
    // simply by using a recursive "call" to out Transform
    // struct
    using OUT = decltype( std::tuple_cat( 
        std::tuple< std::pair< FIRST, SECOND >>(),
        std::declval<typename Transform< std::tuple<SECOND, NEXT, TAIL...>>::OUT>()
        ));        
};

// This specialization is used for the last input as
// it has exactly 2 parameters  
template <typename FIRST, typename SECOND >
struct Transform< std::tuple<FIRST, SECOND >>
{
    using OUT = typename std::tuple<std::pair < FIRST, SECOND>>;
};

using OUT = Transform< input >::OUT;

template < typename T>
void Print()
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

int main()
{
    Print< Transform< input >::OUT >();
}

Нет необходимости определять свой собственный template <typename F, typename S> class trans {...};, как у нас std::pair;

1 голос
/ 10 марта 2020

Вдохновленный ответом Андрея Семашева ... нерекурсивная (и не std::tuple_element) версия.

С учетом некоторой объявленной функции (не требующей определения: используются только внутри decltype())

template <std::size_t N, std::size_t I, typename, typename>
constexpr std::enable_if_t<(I == N), std::tuple<>> filter ();

template <std::size_t N, std::size_t I, typename T1, typename T2>
constexpr std::enable_if_t<(I < N), std::tuple<trans<T1, T2>>> filter ();

template <std::size_t N, typename ... Ts1, typename ... Ts2,
          std::size_t ... Is>
constexpr auto getChain (std::tuple<Ts1...>, std::tuple<Ts2...>,
                         std::index_sequence<Is...>)
   -> decltype(std::tuple_cat(filter<N, Is, Ts1, Ts2>()...));

Вы можете написать trans_chain_create (_t) следующим образом

template <typename T, typename ... Ts>
struct trans_chain_create
 {
   using Tpl1 = std::tuple<T, Ts...>;
   using Tpl2 = std::tuple<Ts..., T>;
   using IndS = std::make_index_sequence<sizeof...(Ts)+1u>;

   using type = decltype(getChain<sizeof...(Ts)>
                   (std::declval<Tpl1>(), std::declval<Tpl2>(), IndS{}));
 };

template <typename ... Args>
using trans_chain_create_t = typename trans_chain_create<Args...>::type;

Ниже приводится полная компиляция (достаточно C ++ 14), пример

#include <tuple>
#include <string>
#include <utility>

template <typename, typename>
class trans
 { };

class json
 { };

template <std::size_t N, std::size_t I, typename, typename>
constexpr std::enable_if_t<(I == N), std::tuple<>> filter ();

template <std::size_t N, std::size_t I, typename T1, typename T2>
constexpr std::enable_if_t<(I < N), std::tuple<trans<T1, T2>>> filter ();

template <std::size_t N, typename ... Ts1, typename ... Ts2,
          std::size_t ... Is>
constexpr auto getChain (std::tuple<Ts1...>, std::tuple<Ts2...>,
                         std::index_sequence<Is...>)
   -> decltype(std::tuple_cat(filter<N, Is, Ts1, Ts2>()...));

template <typename T, typename ... Ts>
struct trans_chain_create
 {
   using Tpl1 = std::tuple<T, Ts...>;
   using Tpl2 = std::tuple<Ts..., T>;
   using IndS = std::make_index_sequence<sizeof...(Ts)+1u>;

   using type = decltype(getChain<sizeof...(Ts)>
                   (std::declval<Tpl1>(), std::declval<Tpl2>(), IndS{}));
 };

template <typename ... Args>
using trans_chain_create_t = typename trans_chain_create<Args...>::type;

template <typename ... Args>
struct holder
 { using chain_type = trans_chain_create_t<Args...>; };

holder<std::string, int, float, std::string, json> h;

int main ()
 {
   using H = holder<std::string, int, float, std::string, json>;
   using CT1 = typename H::chain_type;
   using CT2 = std::tuple<trans<std::string, int>, 
                          trans<int, float>, 
                          trans<float, std::string>, 
                          trans<std::string, json>>;

   static_assert( std::is_same_v<CT1, CT2>, "!" );
 }
1 голос
/ 10 марта 2020

Слишком поздно играть?

Если вы хотите нерекурсивное решение ... std::tuple_element ваш друг. (РЕДАКТИРОВАТЬ: хорошо ... по-видимому, не рекурсивный: как указал Андрей Семашев, std::tuple_element сам, вероятно, является рекурсивным).

С учетом объявленного (наблюдение: не определено; используется только в decltype() ) вспомогательная функция выглядит следующим образом:

template <typename T, std::size_t ... Is>
constexpr auto getChain (std::index_sequence<Is...>)
   -> std::tuple<trans<std::tuple_element_t<Is, T>,
                       std::tuple_element_t<Is+1u, T>>...>;

ваш trans_chain_create_t просто (без рекурсии) становится

template <typename ... Args>
struct trans_chain_create
 { using type = decltype(getChain<std::tuple<Args...>>
                   (std::make_index_sequence<sizeof...(Args)-1u>{})); };

template <typename ... Args>
using trans_chain_create_t = typename trans_chain_create<Args...>::type;

Ниже приводится полная компиляция (достаточно C ++ 14), пример

#include <tuple>
#include <string>
#include <utility>

template <typename, typename>
class trans
 { };

class json
 { };

template <typename T, std::size_t ... Is>
constexpr auto getChain (std::index_sequence<Is...>)
   -> std::tuple<trans<std::tuple_element_t<Is, T>,
                       std::tuple_element_t<Is+1u, T>>...>;

template <typename ... Args>
struct trans_chain_create
 { using type = decltype(getChain<std::tuple<Args...>>
                   (std::make_index_sequence<sizeof...(Args)-1u>{})); };

template <typename ... Args>
using trans_chain_create_t = typename trans_chain_create<Args...>::type;

template <typename ... Args>
struct holder
 { using chain_type = trans_chain_create_t<Args...>; };

holder<std::string, int, float, std::string, json> h;
int main ()
 {
   using H = holder<std::string, int, float, std::string, json>;
   using CT1 = typename H::chain_type;
   using CT2 = std::tuple<trans<std::string, int>, 
                          trans<int, float>, 
                          trans<float, std::string>, 
                          trans<std::string, json>>;

   static_assert( std::is_same_v<CT1, CT2>, "!" );
 }
...