Negate boost range filtered adaptor
Asked Answered
L

2

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
Loreeloreen answered 8/2, 2013 at 9:28 Comment(11)
change it to return i % 2 != 0; instead since it's easier?Karonkaross
the question explicitly aks for not having to do it inside the lambda expression, i'll add a motivation to the question.Loreeloreen
You edited your question after the comment was posted.Karonkaross
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!Loreeloreen
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.Karonkaross
Might sound silly but I wanted to save some typing and not write filtered every time...Loreeloreen
You just need to implement operator! on your adaptor type, such that it returns a negated form of itself. A simple matter of programming.Lexical
@JonathanWakely try it. Not easy to do genericallyPasse
like I said, it's a simple matter of programmingLexical
@JonathanWakely oh lol. That explains. Anyways, I've done a bit of simple programming - it might be enough for the OPPasse
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.Alliterate
P
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)

Passe answered 8/2, 2013 at 11:12 Comment(2)
Great! Your answer just made me take a look at <functional>, 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?Loreeloreen
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.]Passe
E
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

Embed answered 11/1, 2019 at 12:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.