2013-09-13 7 views
2

Я видел, например. соответствующий вопрос на same issue, но у меня другая проблема, которая, я думаю, не может быть решена каким-либо другим способом.Шаблоны шаблонов Variadic variadic, снова

Вот функция any, которая вычисляет ли унарный предикатF верно для любого из элементов в представлении аргументов шаблона пакета (список типов) P:

template <template <typename...> class F, typename P> 
struct any; 

template < 
    template <typename...> class F, 
    template <typename...> class C, typename E, typename... En 
> 
struct any <F, C <E, En...> > : 
    public _if <F <E>{}, _true, any <F, C <En...> > > { }; 

template <template <typename...> class F, template <typename...> class C> 
struct any <F, C <> > : public _false { }; 

где мой _true/_false эквивалентны std::integral_constant <bool, true/false> , и _if <C, T, E> эквивалентно typename std::conditional <C, T, E>::type (подробности не касаются вопроса, см. ниже).

Например, можно написать

template <typename...> struct pack { }; 
template <typename T> using is_int = eq <int, T>; 

any <is_int, pack <int, void, float, double> >(); // evaluates to true 
any <is_int, pack <char, void, float, double> >(); // evaluates to false 

, где eq эквивалентно std::is_same.

Продолжением бинарных предикатов идет следующим образом:

template <template <typename...> class F, typename P, typename Q> 
struct any2; 

template < 
    template <typename...> class F, 
    template <typename...> class C, typename E, typename... En, 
    template <typename...> class D, typename H, typename... Hn 
> 
struct any2 <F, C <E, En...>, D <H, Hn...> > : 
    public _if <F <E, H>{}, _true, any2 <F, C <En...>, D <Hn...> > > { }; 

template < 
    template <typename...> class F, 
    template <typename...> class C, typename Q 
> 
struct any2 <F, C <>, Q> : public _false { }; 

, где мы можем теперь написать

typedef pack <int, void, float, double> A; 
typedef pack <void, float, double, int> B; 
typedef pack <void, float, double, double> C; 

any2 <eq, A, B>(); // false 
any2 <eq, A, C>(); // true 

Здесь возникает вопрос. Можем ли мы расширить подход к n-предикаты префикса, действующие на n ввода "пакеты"?

Данная проблема не совпадает с the previous one, поскольку при оценке F <...> необходим один элемент каждого входного пакета.

Вот вымышленный попытка:

template <template <typename...> class F, typename... P> 
struct any_n; 

template < 
    template <typename...> class F, 
    template <typename...> class... C, typename... E, typename... En 
> 
struct any_n <F, C <E, En...>...> : 
    public _if <F <E...>{}, _true, any_n <F, C <En...>...> > { }; 

template < 
    template <typename...> class F, 
    template <typename...> class C, typename... P 
> 
struct any_n <F, C <>, P...> : public _false { }; 

, который, конечно, не компилировать. Итак, можно написать такие вещи, как C <E, En...>...? Какими будут типы C, E, En?

Я подозреваю, что ответ отрицательный.

Такой синтаксис был бы чрезвычайно удобным, например, в макросах схемы. Я написал реализацию этого синтаксиса на C++ в прошлом, поддерживая до двух уровней, используя символ dots или etc для .... Но было бы совершенно иначе иметь поддержку от компилятора (особенно если вам нужно скомпилировать вещь в тот же день).

ответ

4

Clang 3.3 и GCC 4.8.1 оба признают ваше определение any_n<> без предупреждения, что заставляет меня думать, что это совершенно верно, кроме того, что это логика.


ПОЧЕМУ НЕ РАБОТАЕТ?

В пути any_n<> определена, она требует, чтобы все, кроме первого элемента каждой пачки быть точно таким же, ибо в struct any_n <F, C <E, En...>...>, En... должен быть повторенное для каждого параметра пакетаC<> расширенной.

То есть, учитывая

template<typename...> 
struct Pack; 

typedef Pack<int, float, double, bool> A; 
typedef Pack<void, float, double, bool> B; 
typedef Pack<float, float, double, bool> C; 

и

template<typename...> 
struct SomeMetaFunction 
{...}; 

инстанциации any_n<SomeMetaFunction, A, B, C> успешно связывает параметры шаблонов any_n<> следующим образом:

F => SomeMetaFunction 
C => std::tuple 
E... => [int, void, float] 
F... => [float, double, bool] 

в соответствии со своей второй специализации.

Попытка создать экземпляр any_n<SomeMetaFunction, A, B, C, D> например, где D определяется как

typedef std::tuple <char, int, double, bool> D; 

вызывает undefined any_n<> ошибку, как и ожидалось.


КАК СДЕЛАТЬ ЭТО

Вы можете достичь именно то, что вы хотите лишь незначительные изменения в определении any_n<>. Идея состоит в том, чтобы использовать концепцию рекурсивных списков, как это делают функциональные языки.

Прежде всего, вам понадобится мета-функция для преобразования pack<a, b, c, ..., z> в pack<a, pack<b, c, ... z>>.

template<typename...> 
struct toRecursiveList; 

//a recursive list already 
template<template <typename...> class Pack, typename H, typename... T> 
struct toRecursiveList<Pack<H, Pack<T...>>> 
{ 
    using type = Pack<H, Pack<T...>>; 
}; 

//several elements 
template<template <typename...> class Pack, typename H, typename... T> 
struct toRecursiveList<Pack<H, T...>> 
{ 
    using type = Pack<H, Pack<T...>>; 
}; 

//one element 
template<template <typename...> class Pack, typename H> 
struct toRecursiveList<Pack<H> > 
{ 
    using type = Pack<H, Pack<>>; 
}; 

//empty 
template<template <typename...> class Pack> 
struct toRecursiveList<Pack<>> 
{ 
    using type = Pack<>; 
}; 

//missing pack 
template<typename H, typename... T> 
struct toRecursiveList<H, T...> 
{ 
    template<typename...> 
    struct Pack; 
    using type = Pack<H, Pack<T...>>; 
}; 

Базовый случай any_n<> обеспечивает аргументы преобразуются в рекурсивном списке на первом этапе, если это необходимо.

template <template <typename...> class F, typename... P> 
struct any_n : 
     public any_n<F, typename toRecursiveList<P>::type...> 
{}; 

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

template < 
    template <typename...> class F, 
    template <typename...> class... C, typename... E, typename... En 
> 
struct any_n <F, C <E, En>...> : //two elements each, no need for double pack expansion on En 
    public _if <F <E...>{}, _true, any_n <F, typename toRecursiveList<En>::type...>>::type { }; 
    //            ^ensures a recursive list is passed onto the next recursion step 

Рекурсивный охранник остается неизменным.

template < 
    template <typename...> class F, 
    template <typename...> class C, typename... P 
> 
struct any_n <F, C <>, P...> : public _false { }; 

В предыдущем примере any_n<SomeMetaFunction, A, B, C, D> Теперь компилируется, как и ожидалось.

Для примера SSCCE, пожалуйста, нажмите here.

+0

Tricky! Есть ли у вас пример SSCCE/live? – dyp

+0

@ DyP уверен, встал на [ideone] (http://ideone.com/dFoSmD) – brunocodutra

+0

@brunocodutra Просто просмотрев, я увидел этот ответ, и я не помню, чтобы его видели в это время! Я предпочитаю использовать boost. Мой вопрос был скорее, если это поддерживается языком, и это ясно, и это нужно самому программировать. – iavr

1

Я бы использовал boost::mpl::zip_view, чтобы перебирать все ваши списки сразу. На самом деле, я думаю, что пример в ссылке - именно то, что вам нужно, кроме вместо transform_view вы бы хотели find_if.

#include <boost/mpl/zip_view.hpp> 
    #include <boost/mpl/find_if.hpp> 
    #include <boost/mpl/placeholders.hpp> 
    #include <boost/mpl/unpack_args.hpp> 
    #include <boost/mpl/vector.hpp> 
    #include <boost/mpl/range_c.hpp> 
    #include <boost/mpl/or.hpp> 
    #include <boost/mpl/vector_c.hpp> 
    #include <boost/mpl/equal_to.hpp> 
    #include <boost/mpl/not_equal_to.hpp> 
    #include <boost/mpl/not.hpp> 
    #include <boost/mpl/and.hpp> 
    #include <iostream> 

    using namespace boost::mpl; 
    using namespace boost::mpl::placeholders; 

    template <class Predicate, class ... Sequences> 
    struct any_n 
    { 
     typedef zip_view<vector<Sequences...> > seq; 
     typedef typename not_< 
      typename boost::is_same< 
       typename find_if< 
        seq, 
        unpack_args<Predicate> >::type, 
       typename end<seq>::type>::type>::type type; 
    }; 

    typedef not_equal_to<boost::is_same<_1, _2>, 
         boost::is_same<_2, _3> >pred1; 

    typedef or_<equal_to<_1, _2>, 
       equal_to<_2, _3> > pred2; 

    typedef any_n<pred1, 
        range_c<int,0,10>, 
        vector_c<unsigned, 1, 4, 2, 5>, 
        vector_c<short, 0, 0, 2, 7, 4> >::type found1; 

    typedef any_n<pred2, 
        range_c<int,0,10>, 
        vector_c<unsigned, 1, 4, 2, 5>, 
        vector_c<short, 0, 0, 2, 7, 4> >::type found2; 

    int main() 
    { 
     std::cout << std::boolalpha << found1() << ' ' << found2() << std::endl; 
    } 

Если вы не знакомы с подходом подталкивание к шаблонному метапрограммированию (т.е. Boost.TemplateMetaprogrammingLibrary), то это может показаться несколько подавляющими (или даже очень подавляющими). Единственная книга (по крайней мере, по крайней мере), которую я когда-либо охотно покупала при программировании, была «C++ Template Metaprogramming», и я очень рекомендую ее, если вы заинтересованы в этом.
Я нашел this вопрос, который указывает на эту книгу среди других.

+0

Спасибо. Это действительно приятно, хотя меня интересовало то, что поддерживает язык. В прошлом я разработал свою собственную библиотеку метапрограммирования, которая действительно выглядела как язык ML (и могла бы справиться с этой проблемой также). Было впечатляюще, что вы могли бы выразить в одной строке кода, но если вы увлеклись, вы подождали минут для компиляции ... – iavr

+0

@iavr, хотя язык не поддерживает * двойное расширение пакета * прямо вы можете наверняка обходным путем это, как я пытался показать на мой ответ. Как вы сказали, чтобы решить такие проблемы метапрограммирования, ключ должен думать так же, как и при работе с функциональными языками, например, [S] ML. – brunocodutra

Смежные вопросы