split variadic template arguments

Comment diviser les arguments de modèle variadique en deux moitiés? Quelque chose comme:

template <int d> struct a {
  std::array <int, d> p, q;
  template <typename ... T> a (T ... t) : p ({half of t...}), q ({other half of t...}) {} 
};
23
demandé sur ildjarn 2011-03-30 14:11:34

5 réponses

il nous manque encore beaucoup d'assistants pour manipuler les paquets de paramètres variadiques (ou je n'en ai pas connaissance). Jusqu'à ce qu'une bibliothèque Boost nous les amène, on peut toujours écrire les nôtres.

par exemple, si vous êtes prêt à reporter votre initialisation des tableaux au corps du constructeur, vous pouvez créer et utiliser une fonction qui copie une partie du paquet de paramètres à un itérateur de sortie:

#include <array>
#include <cassert>
#include <iostream>

// Copy n values from the parameter pack to an output iterator
template < typename OutputIterator >
void copy_n( size_t n, OutputIterator )
{
  assert ( n == 0 );
}

template < typename OutputIterator, typename T, typename... Args >
void copy_n( size_t n, OutputIterator out, const T & value, Args... args )
{
  if ( n > 0 )
  {
    *out = value;
    copy_n( n - 1, ++out, args... );
  }
}

// Copy n values from the parameter pack to an output iterator, starting at
// the "beginth" element
template < typename OutputIterator >
void copy_range( size_t begin, size_t size, OutputIterator out )
{
  assert( size == 0 );
}


template < typename OutputIterator, typename T, typename... Args >
void copy_range( size_t begin, size_t size, OutputIterator out, T value, Args... args )
{
  if ( begin == 0 )
  {
    copy_n( size, out, value, args... );
  }
  else
  {
    copy_range( begin - 1, size, out, args... );
  }
}


template < int N > 
struct DoubleArray
{
  std::array< int, N > p;
  std::array< int, N > q;

  template < typename... Args >
  DoubleArray ( Args... args )
  {
    copy_range( 0, N, p.begin(), args... );
    copy_range( N, N, q.begin(), args... );
  } 

};

int main()
{
  DoubleArray<3> mya(1, 2, 3, 4, 5, 6);
  std::cout << mya.p[0] << mya.p[2] << std::endl;
  std::cout << mya.q[0] << mya.q[2] << std::endl;
}

// Ouput:
// 13
// 46

Comme vous pouvez le voir, vous pouvez (pas si) de créer facilement votre propres algorithmes pour manipuler les paquets de paramètres; Tout ce qui est nécessaire est une bonne compréhension de la récursion et de l'appariement des motifs (comme toujours quand on fait de la métaprogrammation de Template)...).

4
répondu Luc Touraille 2011-03-30 13:36:24

la solution de Luc est propre et directe, mais manque cruellement de plaisir.

Parce qu'il n'y a qu'une seule façon correcte d'utiliser les gabarits variadiques et c'est de les abuser pour faire des trucs de métaprogrammation démesurés:)

comme ceci:

template <class T, size_t... Indx, class... Ts>
std::array<T, sizeof...(Indx)>
split_array_range_imp(pack_indices<Indx...> pi, Ts... ts)
{
    return std::array<T, sizeof...(Indx)>{get<Indx>(ts...)...}; //TADA
}


template <class T, size_t begin, size_t end, class... Ts>
std::array<T, end - begin>
split_array_range(Ts... ts)
{
    typename make_pack_indices<end, begin>::type indices;
    return split_array_range_imp<T>(indices, ts...);
}

template <size_t N>
struct DoubleArray
{
  std::array <int, N> p, q;

  template <typename ... Ts>
  DoubleArray (Ts ... ts) :
  p( split_array_range<int, 0                , sizeof...(Ts) / 2 >(ts...) ),
  q( split_array_range<int, sizeof...(Ts) / 2, sizeof...(Ts)     >(ts...) )
  {
  }
};

int main()
{
    DoubleArray<3> mya{1, 2, 3, 4, 5, 6};
    std::cout << mya.p[0] << "\n" << mya.p[1] << "\n" << mya.p[2] << std::endl;
    std::cout << mya.q[0] << "\n" << mya.q[1] << "\n" << mya.q[2] << std::endl;
}

c'est assez court, sauf qu'il faut coder un helper:

tout d'abord, nous avons besoin de la structure make_pack_indices, qui est utilisée pour générer un gamme de entier au moment de la compilation. Par exemple make_pack_indices<5, 0>::type est en fait le type pack_indices<0, 1, 2, 3, 4>

template <size_t...>
struct pack_indices {};

template <size_t Sp, class IntPack, size_t Ep>
struct make_indices_imp;

template <size_t Sp, size_t ... Indices, size_t Ep>
struct make_indices_imp<Sp, pack_indices<Indices...>, Ep>
{
    typedef typename make_indices_imp<Sp+1, pack_indices<Indices..., Sp>, Ep>::type type;
};

template <size_t Ep, size_t ... Indices>
struct make_indices_imp<Ep, pack_indices<Indices...>, Ep>
{
    typedef pack_indices<Indices...> type;
};

template <size_t Ep, size_t Sp = 0>
struct make_pack_indices
{
    static_assert(Sp <= Ep, "__make_tuple_indices input error");
    typedef typename make_indices_imp<Sp, pack_indices<>, Ep>::type type;
};

nous avons aussi besoin d'une fonction get (), très similaire à std::get for tuple, comme std::get<N>(ts...) renvoie le nième élément d'un paquet de paramètres.

template <class R, size_t Ip, size_t Ij, class... Tp>
struct Get_impl
{
    static R& dispatch(Tp...);
};

template<class R,  size_t Ip, size_t Jp, class Head, class... Tp>
struct Get_impl<R, Ip, Jp, Head, Tp...>
{
    static R& dispatch(Head& h, Tp&... tps)
    {
        return Get_impl<R, Ip, Jp + 1, Tp...>::dispatch(tps...);
    }
};

template<size_t Ip, class Head, class... Tp>
struct Get_impl<Head, Ip, Ip, Head, Tp...>
{
    static Head& dispatch(Head& h, Tp&... tps)
    {
        return h;
    }
};


template <size_t Ip, class ... Tp>
typename pack_element<Ip, Tp...>::type&
get(Tp&... tps)
{
    return Get_impl<typename pack_element<Ip, Tp...>::type, Ip, 0, Tp...>::dispatch(tps...);
}

mais pour construire get() nous avons aussi besoin d'une structure d'aide pack_element, encore une fois très similaire à std::tuple_element, tel que pack_element<N, Ts...>::type est le nième type du paquet de paramètres.

template <size_t _Ip, class _Tp>
class pack_element_imp;

template <class ..._Tp>
struct pack_types {};

template <size_t Ip>
class pack_element_imp<Ip, pack_types<> >
{
public:
    static_assert(Ip == 0, "tuple_element index out of range");
    static_assert(Ip != 0, "tuple_element index out of range");
};

template <class Hp, class ...Tp>
class pack_element_imp<0, pack_types<Hp, Tp...> >
{
public:
    typedef Hp type;
};

template <size_t Ip, class Hp, class ...Tp>
class pack_element_imp<Ip, pack_types<Hp, Tp...> >
{
public:
    typedef typename pack_element_imp<Ip-1, pack_types<Tp...> >::type type;
};

template <size_t Ip, class ...Tp>
class pack_element
{
public:
    typedef typename pack_element_imp<Ip, pack_types<Tp...> >::type type;
};

et c'est parti.

En fait, je ne comprends pas vraiment pourquoi pack_element et get() ne sont pas déjà dans la bibliothèque standard. Ces helpers sont présents pour std:: tuple, pourquoi pas pour parameter pack ?

Note: mon implémentation de pack_element et make_pack_indices est une transposition directe de std:: tuple_element et__make _ tuple_ indices implémentation trouvée dans libc++.

10
répondu Thomas Petit 2011-04-01 00:11:07

Notez que dans ce cas particulier, vous pouvez utiliser std::initializer_list :

template<int... Is> struct index_sequence{};

template<int N, int... Is> struct make_index_sequence
{
    typedef typename make_index_sequence<N - 1, N - 1, Is...>::type type;
};

template<int... Is> struct make_index_sequence<0, Is...>
{
    typedef index_sequence<Is...> type;
};

template <int d> struct a {
    std::array <int, d> p, q;

    constexpr a (const std::initializer_list<int>& t) :
        a(t, typename make_index_sequence<d>::type())
    {}

private:
    template <int... Is>
    constexpr a(const std::initializer_list<int>& t, index_sequence<Is...>) :
        p ({{(*(t.begin() + Is))...}}),
        q ({{(*(t.begin() + d + Is))...}})
    {}
};
3
répondu Jarod42 2013-12-29 03:44:44

Voici encore une autre solution:

#include <array>
#include <tuple>
#include <iostream>

template <int i, int o> struct cpyarr_ {
  template < typename T, typename L > static void f (T const& t, L &l) {
    l[i-1] = std::get<i-1+o> (t);
    cpyarr_<i-1,o>::f (t,l);
  }
};

template <int o> struct cpyarr_ <0,o> {
  template < typename T, typename L > static void f (T const&, L&) {}
};

template <int i, int o, typename U, typename ... T> std::array < U, i > cpyarr (U u, T... t) {
  std::tuple < U, T... > l { u, t... };
  std::array < U, i > a; 
  cpyarr_<i,o>::f (l, a); // because std::copy uses call to memmov which is not optimized away (at least with g++ 4.6)
  return a;
}

template <int d> struct a {
  std::array <int, d> p, q;
  template <typename ... T> a (T ... t) : p (cpyarr<d,0> (t...)), q (cpyarr<d,d> (t...)) {} 
};

int main () {
  a <5> x { 0,1,2,3,4,5,6,7,8,9 };
  for (int i = 0; i < 5; i++)
    std::cout << x.p[i] << " " << x.q[i] << "\n";
}
0
répondu Thomas 2011-04-01 17:36:12

je sais que cette question est assez vieux, mais je l'ai trouvé hier en cherchant une solution à un problème très similaire. J'ai moi-même trouvé une solution et j'ai fini par écrire une petite bibliothèque qui fait ce que vous voulez. Vous pouvez trouver une description ici si vous êtes toujours intéressé.

0
répondu Andy Prowl 2017-05-23 12:08:13