Реализация мета-функции zip в С++ 11

Я действительно пытаюсь посмотреть, могу ли я получить минимальную библиотеку, которая поддерживает очень немногие операции, которые я использую в boost:: fusion.

Вот что я до сих пор...

template < typename... Types >
struct typelist
{
};

template < template < typename... > class F, typename... Args >
struct apply
{
  typedef typename F < Args... >::type type;
};

template < typename, template < typename... > class >
struct foreach;

template < typename... Types, template < typename Arg > class F >
struct foreach < typelist < Types... >, F >
{
  typedef typelist < typename apply < F, Types >::type... > type; 
};

Поскольку реализация мета-функции foreach тривиальна, я думал, что zip тоже будет легко. По-видимому, это не так.

template < typename... >
struct zip;

template < typename...  Types0, typename... Types1 >
struct zip < typelist < Types0... >, typelist < Types1... > >
{
  typedef typelist < typelist < Types0, Types1 >... > type;
};

Как я могу обобщить эту мета-функцию zip на произвольное количество списков типов? Что нам нужно здесь, кажется, является пакетом параметров пакетов параметров. Я не уверен, как это сделать.

Изменить 1:

Реализация is_equal...

template < std::size_t... Nn >
struct is_equal;

template < std::size_t N0, std::size_t N1, std::size_t... Nn >
struct is_equal < N0, N1, Nn... >
: and_ <
    typename is_equal < N0, N1 >::type
  , typename is_equal < N1, Nn... >::type
  >::type
{
};

template < std::size_t M, std::size_t N >
struct is_equal < M, N > : std::false_type
{
  typedef std::false_type type;
};

template < std::size_t N >
struct is_equal < N, N > : std::true_type
{
  typedef std::true_type type;
};

Аналогичный подход можно применить и к zip, я думаю... еще не пробовал с zip, но сделаю это, когда вернусь домой.

Изменить 2:

Вот что я, наконец, подумал, выглядел более изящным. Это в основном вариация подхода Вона Катона.

namespace impl
{

template < typename Initial, template < typename, typename > class F, typename... Types >
struct foldl;

template < typename Initial, template < typename, typename > class F, typename First, typename... Rest >
struct foldl < Initial, F, First, Rest... >
{
  typedef typename foldl < typename F < Initial, First >::type, F, Rest... >::type type;
};

template < typename Final, template < typename, typename > class F >
struct foldl < Final, F >
{
  typedef Final type;
};

template < typename Type, typename TypeList >
struct cons;

template < typename Type, typename... Types >
struct cons < Type, typelist < Types... > >
{
  typedef typelist < Types..., Type > type;
};

template < typename, typename >
struct zip_accumulator;

template < typename... Types0, typename... Types1 >
struct zip_accumulator < typelist < Types0... >, typelist < Types1... > >
{
  typedef typelist < typename cons < Types1, Types0 >::type... > type;
};

template < typename... Types0 >
struct zip_accumulator < typelist <>, typelist < Types0... > >
{
  typedef typelist < typelist < Types0 >... > type;
};

template < typename... TypeLists >
struct zip
{
  typedef typename foldl < typelist <>, zip_accumulator, TypeLists... >::type type;
};

}

template < typename... TypeLists >
struct zip
{
  static_assert(and_ < typename is_type_list < TypeLists >... >::value, "All parameters must be type lists for zip");
  static_assert(is_equal < TypeLists::length... >::value, "Length of all parameter type lists must be same for zip");
  typedef typename impl::zip < TypeLists... >::type type;
};

template < typename... TypeLists >
struct zip < typelist < TypeLists... > > : zip < TypeLists... >
{
};

Это относится к zip как к операции fold.

Ответы

Ответ 1

Это самая короткая реализация, которую я обнаружил:

template <typename...> struct typelist { };   
template <typename A,typename B> struct prepend;
template <typename A,typename B> struct joincols;
template <typename...> struct zip;    

template <typename A,typename... B>
struct prepend<A,typelist<B...> > {
  typedef typelist<A,B...> type;
};

template <>
struct joincols<typelist<>,typelist<> > {
  typedef typelist<> type;
};

template <typename A,typename... B>
struct joincols<typelist<A,B...>,typelist<> > {
  typedef typename
    prepend<
      typelist<A>,
      typename joincols<typelist<B...>,typelist<> >::type
    >::type type;
};

template <typename A,typename... B,typename C,typename... D>
struct joincols<typelist<A,B...>,typelist<C,D...> > {
  typedef typename
    prepend<
      typename prepend<A,C>::type,
      typename joincols<typelist<B...>,typelist<D...> >::type
    >::type type;
};

template <>
struct zip<> {
  typedef typelist<> type;
};

template <typename A,typename... B>
struct zip<A,B...> {
  typedef typename joincols<A,typename zip<B...>::type>::type type;
};

Ответ 2

Кажется, что это выполнимо с полными списками (что означает операции с головкой, хвостом и минусами) и рекурсия. Протестировав снимок GCC 4.7, весь материал std находится в <type_traits>:

struct nil {};

template<typename T>
struct is_nil: std::is_same<T, nil> {};

template<typename... T>
struct and_: std::true_type {};

template<typename First, typename... Rest>
struct and_<First, Rest...>
: std::integral_constant<
    bool
    , First::value && and_<Rest...>::value
> {};

template<typename T>
struct not_
: std::integral_constant<bool, !T::value> {};

template<typename... T>
struct typelist;

template<typename First, typename Second, typename... Rest>
struct typelist<First, Second, Rest...> {
    typedef First head;
    typedef typelist<Second, Rest...> tail;
};

template<typename Last>
struct typelist<Last> {
    typedef Last head;
    typedef nil tail;
};

template<typename T, typename List>
struct cons;

template<typename T, typename... Ts>
struct cons<T, typelist<Ts...>> {
    typedef typelist<T, Ts...> type;
};

// workaround for:
// sorry, unimplemented: cannot expand '...' into a fixed-length argument list
template<template<typename...> class Template, typename... T>
struct gcc_workaround {
    typedef Template<T...> type;
};

namespace detail {

template<typename Sfinae, typename... Lists>
struct zip;

template<typename... Lists>
struct zip<
    typename std::enable_if<and_<is_nil<typename Lists::tail>...>::value>::type
    , Lists...
> {
    typedef typelist<typelist<typename Lists::head...>> type;
};

template<typename... Lists>
struct zip<
    typename std::enable_if<and_<not_<is_nil<typename Lists::tail>>...>::value>::type
    , Lists...
> {
    typedef typename cons<
        typelist<typename Lists::head...>
        , typename gcc_workaround<zip, void, typename Lists::tail...>::type::type
    >::type type;
};

} // detail

template<typename... Lists>
struct zip: detail::zip<void, Lists...> {};

Возможно, вы захотите добавить проверку ошибок ко всему этому (я думаю о недействительных экземплярах, которые в настоящее время просто оставлены как неполные типы). Честно говоря, сколько времени мне потребовалось, чтобы понять это, я бы посоветовал придерживаться Boost.MPL. Такие вещи, как ленивая оценка (с которой мне не нужно было делать вещи SFINAE), являются благом, и я не хотел бы изобретать их. Плюс в тот день, когда С++ 11 позволил вам получить лучшее из обоих миров.


Я забыл упомянуть о том, что Boost.MPL также имеет преимущества родословности. Он может работать на любом типе, который удовлетворяет одной из его концепций последовательности (также возможно не навязчиво адаптировать существующий тип), тогда как вы вынуждаете использовать typelist.