1

Consider the following attempt at a Boost.MPL style metaprogramming version of 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;
    }

This prints out: 1 1 1 1 1 1 1 0. I.e., the final call to all_of with std::is_base_of passed as a predicate generates false. Why does this not work? Apperently, the base class B does not get properly bound to the predicate. How should I pass a binary predicate? Some combination of mpl::lambda or mpl::bind?

UPDATE

Based on Luc Touraille's excellent answer, here is the lambda-free solution to my question, with as an added bonus the compile-time versions of none_of and 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 > >
    {};
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • 1
    It does not work in the first case either: if you add a non-pod in `pod_types`, you'll see that `all_of` still returns true. – Luc Touraille Feb 22 '12 at 08:24
  • @Luc Touraille One can never test enough! I should have tested a negative outcome as well. Funny that unary predicates map to true and binary predicates map to false. – TemplateRex Feb 22 '12 at 10:32

2 Answers2

1

You need to turn the predicate into a lambda or _1 will be interpreted as the first level of calls of the fold instead of the first parameter to be passed to Pred. mpl::lambda is what you need

Joel Falcou
  • 6,247
  • 1
  • 17
  • 34
  • Where should a lambda be inserted? I tried both at the call site, and in the definition of all_of, but no success. And why does the unary predicate is_pod work as it is, even with a _1 passed as a parameter? – TemplateRex Feb 19 '12 at 21:30
1

Here is a solution using find_if instead of 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
    >
{};

If you want to stick to fold, you'll need to turn the predicate into a metafunction using lambda before invoking it, to protect the arguments:

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

However, you'll note that this won't work either. I'm not sure why exactly, I think it is related to this discussion on the Boost mailing list. Apparently, there is an issue with the arity of apply which is greater than the one supported by lambda. Anyway, a simple workaround is to use apply1 instead of apply. Here is the full solution:

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
            >
        >
    >
{};
Luc Touraille
  • 79,925
  • 15
  • 92
  • 137
  • Thanks! This is what I was looking for. Wouldn't it still be convenient to also have a `mpl::lambda` wrapping the placeholders inside the `mpl::and_` in the above code? Just in case you want to pass the `all_of` itself in a lambda expression? Or is this best done by outside callers? – TemplateRex Feb 22 '12 at 10:35
  • Hum, I'm don't think this is necessary.As it is, `all_of` is a binary metafunction like any other, that can be used either with arguments or with placeholders. As far as I can tell, there is no risk of conflicting placeholders. I'm not completely sure about this, perhaps @JoelFalcou could confirm? (Hi Joel, btw!) – Luc Touraille Feb 22 '12 at 11:02
  • 1
    If I understand correctly, the problem you had was similar to the one described in [this thread](http://lists.boost.org/Archives/boost/2012/01/189614.php), namely nested placeholder expressions. Indeed, the instantiation of `all_of` with `is_pod` was equivalent to `fold< pod_types, true_, and_< _1, apply< is_pod< _1 >, _2 > > >`. The two `_1` was not meant to refer to the same arguments, hence the need to protect `is_pod< _1 >` with `lambda`. – Luc Touraille Feb 22 '12 at 13:05
  • 1
    If you create a placeholder expression with the last version of `all_of` I gave, and invoke it (e.g. `typedef all_of< _1, _2 > all_of_lambda; bool b = apply< all_of_lambda, pod_types, is_pod< _1 > >::type::value;`), you don't have any problem and obtain the good result. I must admit that I don't really understand why, though... – Luc Touraille Feb 22 '12 at 13:26
  • 1
    Sometimes I feel using Boost.MPL is walking on a tight mountain ledge: there's a beautiful view into the valley below, but put one step wrong... :-) – TemplateRex Feb 22 '12 at 13:45
  • 1
    After a bit more reflection, I think there is no issue in the case described above because `all_of` *inherits* from `fold`: they are not the same type. Hence, `all_of< _1, _2 >` is *not* the same as `fold< _1, true_, and_< _1, apply1< lambda< _2 >::type, _2 > > >` (which would obviously won't work). It would be interesting to check the behavior when using C++11 template aliases. – Luc Touraille Feb 22 '12 at 13:48
  • I tested on GCC 4.7, it works with template aliases as well. I'm still a bit lost here :/! If you find more information, feel free to share :). – Luc Touraille Feb 22 '12 at 14:05