1

I want to know if it is possible to chain a bunch of lambdas so that I can use them all at once, something like this:

std::vector<int> vec{1, 2, 3};
auto f1 = [](int i){return i == 1;};
auto f2 = [](int i){return i == 2;};

// What template type can I use for logical_or ?
auto f = combine(std::logical_or, f1, f2); 

vec.erase(std::remove_if(vec.begin(), vec.end(), f), vec.end());

So far I was thinking something like this:

template <typename OP, typename T>
T combine(const OP& op, const T& t1, const T& t2) {
    return t1 op t2;
}

template <typename OP, typename T, typename... Ts>
T combine(const OP& op, const T& t1, const T& t2, const Ts&... ts) {
    return t1 op t2 op combine(op, ts...);
}

But lambda || lambda doesn't make sense. Is what I'm trying to do possible?

6
  • 1
    You need to call the lambdas with arguments: op(t1(arg), t2(arg)) Commented Apr 13, 2018 at 2:20
  • 1
    how about auto f = [&](int i) { return f1(i) || f2(i); }; ? Commented Apr 13, 2018 at 2:36
  • Am I the only one who thinks the code vec.erase(std::remove_if(vec.begin(), vec.end(), f), vec.end()); is problematic? The outer erase call seems to be useless. Commented Apr 13, 2018 at 2:39
  • @Lingxi: Look at Erase-Remove idiom Commented Apr 13, 2018 at 2:41
  • @Jarod42: Oh. My bad. Commented Apr 13, 2018 at 2:42

3 Answers 3

3

You might create class to do the job:

template <typename ... Fs>
struct AnyOf
{
public:
    AnyOf(Fs... fs) : fs(fs...) {};

    template <typename ...Ts>
    decltype(auto) operator()(Ts&&... args) const
    {
        return call(std::index_sequence_for<Fs...>(), std::forward<Ts>(args)...);
    }

private:
    template <std::size_t ...Is, typename ...Ts>
    decltype(auto) call(std::index_sequence<Is...>, Ts&&... args) const
    {
        return (... || std::get<Is>(fs)(args...));
    }
private:
    std::tuple<Fs...> fs;
};

With usage:

auto f = AnyOf(f1, f2); // C++17 way.
                        //previously, template function `MakeAnyOf` should be implemented

Demo

Sign up to request clarification or add additional context in comments.

2 Comments

That looks very cool!! The fold statement in the call function is C++17 only right? Is it possible to do something like that in C++14?
Yes it is fold expression, you can do similar code using c++14 only but it is more verbose (and extra difficulty is to respect short-circuit evaluation).
2

try this functional one-liner (it use fold expression so it's c++17)

auto any_combiner = [](auto&& ...fs){return [=](auto&& ...params){return (fs(params...) ||...);};}; 
auto any = any_combiner(f1,f2);

Wandbox example


or you can simply use std::any_of for || (and std::all_of for &&), and it's c++11

vector<function<bool(int)>> fs = {f1,f2};
auto any = [&](int i){return std::any_of(fs.begin(),fs.end(),[i](function<bool(int)>&f){return f(i);});};

or what's wrong with plain old for, it so simple! (basically std::any_of)

vector<function<bool(int)>> fs = {f1,f2};
auto any = [&](int i){
    for(auto& f:fs)
        if(f(i))
            return true;
    return false;
};

Comments

1
template<class F>
struct logical_f;
template<class F>
logical_f<F> logical( F f ){
  return std::move(f);
}
template<class F>
struct logical_f:F{
  logical_f(F in):F(std::move(in)){}
  template<class O>
  friend auto operator||( logical_f self, logical_f<O> const& o ) {
    auto r = [lhs=std::move(self.f), rhs=o.f](auto&&...args)->bool{
      return lhs(args...)||rhs(args...);
    };
    return logical(std::move(r));
};

now you can do:

auto f = logical(f1) || logical(f2); 

In you can fold over ||; prior to that you can use hand written functions that do the folding.

1 Comment

Very helpful! Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.