5

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
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
gnzlbg
  • 7,135
  • 5
  • 53
  • 106
  • change it to `return i % 2 != 0;` instead since it's easier? – Rapptz Feb 08 '13 at 09:29
  • the question explicitly aks for not having to do it inside the lambda expression, i'll add a motivation to the question. – gnzlbg Feb 08 '13 at 09:30
  • You edited your question after the comment was posted. – Rapptz Feb 08 '13 at 09:31
  • Sorry for that! I truly thought the remark about not doing it inside the lambda was in the original question. I've added a motivation to the question to put things into context. I hope the intent is now clear. Thanks! – gnzlbg Feb 08 '13 at 09:35
  • Going to play devil's advocate again. Why not just do `for(auto& i : v | filtered(!is_even))`? I actually think that's the cleanest solution I can see. – Rapptz Feb 08 '13 at 09:37
  • Might sound silly but I wanted to save some typing and not write filtered every time... – gnzlbg Feb 08 '13 at 09:46
  • You just need to implement `operator!` on your adaptor type, such that it returns a negated form of itself. A simple matter of programming. – Jonathan Wakely Feb 08 '13 at 10:18
  • @JonathanWakely try it. Not easy to do generically – sehe Feb 08 '13 at 10:26
  • like I said, it's a [simple matter of programming](http://www.catb.org/jargon/html/S/SMOP.html) – Jonathan Wakely Feb 08 '13 at 10:31
  • @JonathanWakely oh lol. That explains. Anyways, I've done a bit of simple programming - it might be enough for the OP – sehe Feb 08 '13 at 11:14
  • 2
    I would also recommend negating the predicate, not the filter adaptor, as long as there is still a concise way of writing new adaptors. I.e. I think `const auto not_even = filter(not_(even));` is really as clear at it can get. – Luc Danton Feb 08 '13 at 11:22

2 Answers2

7

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)

sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    Great! Your answer just made me take a look at , i guess std::not1 would work too. It has been really instructive. Shouldn't I have a bad feeling about putting stuff into detail namespaces of boost? – gnzlbg Feb 08 '13 at 13:44
  • 1
    generally, yeah, it's not the explicitely documented interface. I'd prefer to go with @LucDanton's remark and just invert the _predicate_ instead of the filter (makes more sense too). _[ In this particular instance though, I think you're operating on a semi-stable 'line' that serves extension of Boost Range (at least documented by convention) so it's not so bad.]_ – sehe Feb 08 '13 at 21:02
2

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

zett42
  • 25,437
  • 3
  • 35
  • 72