Как двоичные предикаты должны передаваться в определяемый пользователем алгоритм Boost.MPL? - PullRequest
1 голос
/ 19 февраля 2012

Рассмотрим следующую попытку для версии метапрограммирования в стиле Boost.MPL std::any_of

    #include <iostream>                     // cout
    #include <type_traits>                  // is_base_of, is_pod
    #include <boost/mpl/apply.hpp>          // apply
    #include <boost/mpl/fold.hpp>           // fold
    #include <boost/mpl/lambda.hpp>         // lambda, _1, _2
    #include <boost/mpl/logical.hpp>        // and_, true_   
    #include <boost/mpl/vector.hpp>         // vector

    template
    <
            typename Sequence,
            typename Pred
    >
    struct all_of
    :
            boost::mpl::fold<
                    Sequence,
                    boost::mpl::true_,
                    boost::mpl::lambda<
                            boost::mpl::and_<
                                    boost::mpl::_1,
                                    boost::mpl::apply< Pred, boost::mpl::_2 >
                            >
                    >
            >
    {};

    typedef int P1; typedef char P2; typedef float P3;

    typedef boost::mpl::vector<
            P1, P2, P3
    > pod_types;

    struct B {}; struct D1: B {}; struct D2: B {}; struct D3: B {};

    typedef boost::mpl::vector<
            D1, D2, D3
    > derived_types;

    int main() 
    {
            std::cout << (std::is_pod<P1>::value) << '\n';  // true
            std::cout << (std::is_pod<P2>::value) << '\n';  // true
            std::cout << (std::is_pod<P3>::value) << '\n';  // true       

            std::cout << (
                    all_of<
                            pod_types, 
                            std::is_pod< boost::mpl::_1 >                        
                    >::type::value  // true
            ) << '\n';

            std::cout << (std::is_base_of<B, D1>::value) << '\n';   // true
            std::cout << (std::is_base_of<B, D2>::value) << '\n';   // true
            std::cout << (std::is_base_of<B, D3>::value) << '\n';   // true

            std::cout << (
                    all_of<
                            derived_types, 
                            std::is_base_of< B, boost::mpl::_1 >    
                    >::type::value  // false (but should be true)
            ) << '\n';

            return 0;
    }

Это распечатывает: 1 1 1 1 1 1 1 0. Т.е., последний вызов all_ofс std::is_base_of, передаваемым в качестве предиката, генерирует false.Почему это не работает?По-видимому, базовый класс B неправильно привязан к предикату.Как мне передать двоичный предикат?Некоторая комбинация mpl :: lambda или mpl :: bind?

ОБНОВЛЕНИЕ

Основываясь на превосходном ответе Люка Турэля, вот без лямбда-решения моего вопросав качестве дополнительного бонуса версии компиляции none_of и any_of

    template<typename Sequence, typename Pred>
    struct all_of
    :
            std::is_same< typename 
                    boost::mpl::find_if<
                            Sequence,
                            boost::mpl::not_<Pred>
                    >::type, typename 
                    boost::mpl::end<Sequence>::type
            >
    {};

    template<typename Sequence, typename Pred>
    struct none_of
    :
            all_of< Sequence, boost::mpl::not_< Pred > >
    {};

    template<typename Sequence, typename Pred>
    struct any_of
    :
            boost::mpl::not_< none_of< Sequence, Pred > >
    {};

Ответы [ 2 ]

1 голос
/ 22 февраля 2012

Вот решение, использующее find_if вместо fold:

#include <type_traits>
#include <boost/mpl/end.hpp>
#include <boost/mpl/find_if.hpp>
#include <boost/mpl/logical.hpp>

template
<
    typename Sequence,
    typename Pred
>
struct all_of
:
    std::is_same< typename
        boost::mpl::end< Sequence >::type, typename
        boost::mpl::find_if<
            Sequence,
            boost::mpl::not_< Pred >
        >::type
    >
{};

Если вы хотите придерживаться fold, вам нужно превратить предикат в метафункцию, используя lambda, прежде чем вызывать его, до protect аргументы:

boost::mpl::apply< typename
    boost::mpl::lambda< Pred >::type,
    boost::mpl::_2
>

Однако вы заметите, что это тоже не сработает. Я не уверен, почему именно, я думаю, что это связано с этим обсуждением в списке рассылки Boost . Очевидно, есть проблема с арностью apply, которая больше, чем поддерживаемая lambda. В любом случае, простой обходной путь - использовать apply1 вместо apply. Вот полное решение:

template
<
        typename Sequence,
        typename Pred
>
struct all_of
  : boost::mpl::fold<
        Sequence,
        boost::mpl::true_,
        boost::mpl::and_<
            boost::mpl::_1,
            boost::mpl::apply1< typename
                boost::mpl::lambda< Pred >::type,
                boost::mpl::_2
            >
        >
    >
{};
1 голос
/ 19 февраля 2012

Вам нужно превратить предикат в лямбду, или _1 будет интерпретироваться как первый уровень вызовов сгиба вместо первого параметра, передаваемого в Pred.mpl :: lambda - это то, что вам нужно

...