Реализация мета-функции zip в c ++ 11 - PullRequest
16 голосов
/ 24 сентября 2011

Я на самом деле пытаюсь выяснить, могу ли я получить минимальную библиотеку, которая поддерживает очень мало операций, которые я использую, от 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 операцию.

Ответы [ 2 ]

6 голосов
/ 25 сентября 2011

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

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;
};
3 голосов
/ 24 сентября 2011

Кажется выполнимым с помощью полноценных списков (что означает операции на голове, хвосте и минусах) и рекурсии. Протестировано со снимком 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), - благо, и я не хотел бы их заново изобретать. Кроме того, в день, когда C ++ 11 позволил, вы пожинаете лучшее из обоих миров.


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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...