Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Negate boost range filtered adaptor

Is it possible/achievable to negate a boost filtered adaptor, e.g.

std::vector<int> v = {1, 2, 3, 4, 5};
for(auto i : v | !filtered(is_even))
    std::cout << i << std::endl; // prints 1,3,5

instead of doing the negation inside the lambda expression?

Motivation: I work a lot with filtered and lambda functions, however when I use a filter more than once I usually refactor it into a custom filter, e.g.

for(auto i : v | even) // note: my filters are more complex than even.
    std::cout << i << std::endl; // prints 2,4

Right now when I need the negation I am building a custom filter for them, e.g.

for(auto i : v | not_even)
    std::cout << i << std::endl; // prints 1,2,3

but I would find it nicer to just be able to negate a filter, e.g.

for(auto i : v | !even)
    std::cout << i << std::endl; // prints 1,2,3
like image 220
gnzlbg Avatar asked Feb 08 '13 09:02

gnzlbg


2 Answers

Here's what I came up with on short notice:

#include <boost/range/adaptors.hpp>
#include <boost/functional.hpp>
#include <iostream>

namespace boost { 
    namespace range_detail { 

        template <typename T>
            auto operator!(filter_holder<T> const& f) -> decltype(adaptors::filtered(boost::not1(f.val)))
            {
                return adaptors::filtered(boost::not1(f.val));
            }
    }
}

int main()
{
    using namespace boost::adaptors;
    int const v[] = { 1, 2, 3, 4 };

    std::function<bool(int)> ll = [](int i){return 0 == (i%2);}; // WORKS
    // bool(*ll)(int) = [](int i){return 0 == (i%2);};           // WORKS
    // auto ll = [](int i){return 0 == (i%2);};                  // not yet

    auto even = filtered(ll);

    for (auto i : v | !even)
    {
        std::cout << i << '\n';
    }
}

See it live on liveworkspace.org

Note that it currently handles predicates of the form function pointer and std::function<...>, but not naked lambdas yet (on GCC 4.7.2)

like image 168
sehe Avatar answered Oct 27 '22 08:10

sehe


This doesn't excactly answer the question, because it doesn't negate the filter, but only the predicate. I'm still posting this, because searching for the solution brought this question up as the first result.

Compared to the other answer, this has the advantage that we don't need to add custom code to namespace boost::range_detail.

C++17 Solution

The function std::not_fn can be used to create a negated predicate.

#include <boost/range/adaptors.hpp>
#include <functional>
#include <iostream>

struct is_even
{
    bool operator()( int x ) const { return x % 2 == 0; }
};

int main()
{
    using namespace boost::adaptors;
    int const v[] = { 1, 2, 3, 4 };

    for( auto i : v | filtered( std::not_fn( is_even{} ) ) )
    {
        std::cout << i << ' ';
    }
}

Live Demo

C++11, C++14 Solution

The function std::not1 can be used to create a negated predicate. It has the additional requirement, that the predicate must define a member type, argument_type which has the same type as the predicates operator() parameter.

#include <boost/range/adaptors.hpp>
#include <functional>
#include <iostream>

struct is_even
{
    using argument_type = int;
    bool operator()( int x ) const { return x % 2 == 0; }
};

int main()
{
    using namespace boost::adaptors;
    int const v[] = { 1, 2, 3, 4 };

    for( auto i : v | filtered( std::not1( is_even{} ) ) )
    {
        std::cout << i << ' ';
    }
}

Live Demo

like image 36
zett42 Avatar answered Oct 27 '22 08:10

zett42