How to combine a function and a predicate in for_each?
Solution 1
To use a regular for_each with an if you would need a Functor that emulates an if condition.
#include <algorithm>
#include <vector>
#include <functional>
#include <iostream>
#include <boost/bind.hpp>
using namespace std;
struct incr {
typedef void result_type;
void operator()(int& i) { ++i; }
};
struct is_odd {
typedef bool return_type;
bool operator() (const int& value) {return (value%2)==1; }
};
template<class Fun, class Cond>
struct if_fun {
typedef void result_type;
void operator()(Fun fun, Cond cond, int& i) {
if(cond(i)) fun(i);
}
};
int main() {
vector<int> vec;
for(int i = 0; i < 10; ++i) vec.push_back(i);
for_each(vec.begin(), vec.end(), boost::bind(if_fun<incr, is_odd>(), incr(), is_odd(), _1));
for(vector<int>::const_iterator it = vec.begin(); it != vec.end(); ++it)
cout << *it << " ";
}
Unfortunately my template hackery isn't good enough to manage this with bind1st and bind2nd as it somehow gets confusing with the binder being returned being a unary_function
but it looks pretty good with boost::bind
anyhow. My example is no means perfect as it doesn't allow the Func passed into if_fun to return and I guess somebody could point out more flaws. Suggestions are welcome.
Solution 2
Imitating STL-like algorithms is exactly what you should be doing. That's why they're in the STL.
Specifically, you can use a functor instead of creating an actual function and binding it. This is much neater, really.
template<typename Iterator, typename Pred, typename Operation> void
for_each_if(Iterator begin, Iterator end, Pred p, Operation op) {
for(; begin != end; begin++) {
if (p(*begin)) {
op(*begin);
}
}
}
struct colorequals {
colorequals(int newcol) : color(newcol) {}
int color;
bool operator()(Shape& s) { return s.color == color; }
};
struct displayshape {
void operator()(Shape& s) { // display the shape }
};
for_each_if(shapes.begin(), shapes.end(), colorequals(0), displayshape());
This is usually considered the idiomatic way to go.
Solution 3
Using boost range adaptors is much neater.
using boost::adaptor::filtered;
using boost::bind;
class Shape {
int color() const;
};
void displayShape(const Shape & c);
bool test_color(const Shape & s, int color ){
return s.color() == color;
}
boost::for_each
( vec | filtered(bind(&test_color, _1, 1)
, bind(&displayShape, _1)
)
Note the use of the new range library to abstract away iterators in favor of ranges and the range adaptors library to compose a pipeline of operations.
All the standard stl iterator based algorithms have been ported to range based algorithms.
Imagine this
typedef boost::unordered_map<int, std::string> Map;
Map map;
...
using boost::adaptor::map_keys;
using boost::bind
using boost::ref
using boost::adaptor::filtered;
bool gt(int a, int b)
{ return a > b };
std::string const & get(const Map & map, int const & a)
{ return map[a] }
// print all items from map whose key > 5
BOOST_FOREACH
( std::string const & s
, map
| map_keys
| filtered(bind(>, _1, 5))
| transformed(bind(&get, ref(map), _1))
)
{
cout << s;
}
Read Range Adaptors and Range Algorithm.
Solution 4
You can use the C++20 ranges. Here an example where we add one to all even numbers of a std::vector
#include <ranges>
#include <algorithm>
#include <vector>
namespace ranges = std::ranges;
std::vector<int> vec = {1, 2, 3, 4, 5};
const auto even = [](int i) { return 0 == i % 2; };
ranges::for_each(vec | std::views::filter(even), [](int& i){ i+=1;});
You can find a living example on compiler explorer here
Grim Fandango
Updated on June 06, 2022Comments
-
Grim Fandango almost 2 years
How can you call a
Function
over some part of a container, usingfor_each()
?I have created a
for_each_if()
to do afor( i in shapes ) if( i.color == 1 ) displayShape(i);
and the call looks like
for_each_if( shapes.begin(), shapes.end(), bind2nd( ptr_fun(colorEquals), 0 ), ptr_fun( displayShape ) ); bool colorEquals( Shape& s, int color ) { return s.color == color; }
However, I feel immitating STL-like algorithms is not something that I should be doing.
Is there a way to use only existing STL keywords to produce this ?
I did not want to do a
for_each( shapes.begin(), shapes.end(), bind2nd( ptr_fun(display_shape_if_color_equals), 0 ) );
because, in a more complicated case, the functor name would be misleading with respect to what the functor
*Is there a way to access a struct's member (like
colorEquals
) for functions likefor_each
without having to create a function ? *
-
Grim Fandango almost 14 yearsI have to work with ~10 c++ compilers, some dating back to late 90's. STL implementation in many of them is so diverse, which implies that S in STL does not stand for 'Standard'. That's why I tend to avoid immitating STL functions, and rather just call them. I thought that if for_each_if() is the way to solve the problem, then it should have already been in STL, shouldn't it? (I mean remove_if, find_if, count_if is there already).
-
Puppy almost 14 years@Grim: People normally just stick the if in their functor for for_each. There's no need for such a for_each_if construct.
-
Grim Fandango almost 14 yearsCool. I suppose I can now add a helper function to instantiate an
if_fun
object so that I can reduce the template parameters when callingfor_each()
... -
bradgonesurfing almost 14 yearsboost::bind works equally well with normal functions so the extra noise of class based Functors is not really necessary
-
just somebody about 12 yearsthe automatic url->a href conversion skipped the algorithms one, presumably because of length.
-
newhouse about 7 yearsThis solution is hell of an unituitive mess. Boost adaptors and range is what you want to use here.
-
Felix Dombek almost 2 yearsI would love to see this answer updated to modern C++.