
Hi, I have a project with many occurences of while loops iterating through a container, and operating on the data. I'd like to use for_each() and turn them into one-liners, but I don't like writing one-off functor classes. I was hoping boost::bind could help, but I've read the docs, scratched my head, and failed to work out how the examples apply to my case. Maybe they don't and I'm trying to use it for something it cannot do? See an example below. My function currently looks something like grab_keys() and I want it to look like grab_keys2(). Could some kind soul show me how to write this? (or explain why it cannot be written). Thanks, Darren ------------------------- class Info{ public: int key; std::set<int> points; }; class InfoContainer{ public: Info* get(int); }; void grab_keys(const InfoContainer &cs,const Info *c){ std::set<int> list; std::set<int>::const_iterator i=c->points.begin(); while(i!=c->points.end()){ Info *ec=cs.get(*i++); list.add(ec->key); //Previous two lines could be written: list.add(cs.get(*i++)->key); } //Do something with list here. } void grab_keys2(const AllInfo &cs,const Info *c){ Points list; std::for_each(c->points.begin(),c->points.end(), boost::bind( /* what goes here? */ ) ); //Do something with list here. }

On Monday 25 August 2003 07:05 am, Darren Cook wrote:
class Info{ public: int key; std::set<int> points; };
class InfoContainer{ public: Info* get(int); };
void grab_keys(const InfoContainer &cs,const Info *c){ std::set<int> list; std::set<int>::const_iterator i=c->points.begin(); while(i!=c->points.end()){ Info *ec=cs.get(*i++); list.add(ec->key); //Previous two lines could be written: list.add(cs.get(*i++)->key); } //Do something with list here. }
grab_keys2 would look like this: void grab_keys2(const AllInfo &cs,const Info *c){ Points list; std::for_each(c->points.begin(),c->points.end(), boost::bind(&Points::add, boost::bind(&Info::key, boost::bind(&InfoContainer::get, &cs, _1)))); However, if Points is std::set<int>, then "add" should be "insert" and you have a problem because std::set<int>::insert is overloaded. So let's look at what you're trying to do: You want to map each elements of points to an "Info" pointer via InfoContainer, access the 'key' element through that Info pointer, and put all of those 'key' elements into a std::set, right? Well, that's a transformation on the original sequence of points, so use std::transform like this: void grab_keys3(InfoContainer& cs, const Info* c) { std::set<int> list; std::transform(c->points.begin(), c->points.end(), std::inserter(list, list.end()), boost::bind(&Info::key, boost::bind(&InfoContainer::get, &cs, _1))); } Doug

while(i!=c->points.end()){ Info *ec=cs.get(*i++); list.add(ec->key); //Previous two lines could be written: list.add(cs.get(*i++)->key); }
grab_keys2 would look like this:
void grab_keys2(const AllInfo &cs,const Info *c){ Points list; std::for_each(c->points.begin(),c->points.end(), boost::bind(&Points::add, boost::bind(&Info::key, boost::bind(&InfoContainer::get, &cs, _1))));
Thanks for the reply. I can almost match that up to the parts in the while loop, except mention of "list" is missing. Should it be: boost::bind(&Points::add, &list, boost::bind(&Info::key, boost::bind(&InfoContainer::get, &cs, _1))));
However, if Points is std::set<int>, then "add" should be "insert"
Sorry. Points is a class that can be thought of as a wrapper for std::set. I thought I could simplify the example by substituting, but of course I hadn't thought it through properly had I. BTW, I was hoping I'd be able to do something like this: std::for_each(c->points.begin(),c->points.end(), list.add(cs.get( _1 )->key) ); But after some experiments with boost::lambda I see why this isn't possible. I think if just one call to bind is possible then it is worth it, but when bind is required 2 or more times, falling back to a while loop or a functor seems better. Is anyone else using a similar guideline? Darren

On Monday 25 August 2003 10:21 pm, Darren Cook wrote:
Thanks for the reply. I can almost match that up to the parts in the while loop, except mention of "list" is missing. Should it be:
boost::bind(&Points::add, &list, boost::bind(&Info::key, boost::bind(&InfoContainer::get, &cs, _1))));
Yes, that's correct.
BTW, I was hoping I'd be able to do something like this: std::for_each(c->points.begin(),c->points.end(), list.add(cs.get( _1 )->key) );
This is why we want lambda support in the language :)
But after some experiments with boost::lambda I see why this isn't possible. I think if just one call to bind is possible then it is worth it, but when bind is required 2 or more times, falling back to a while loop or a functor seems better. Is anyone else using a similar guideline?
Darren
I still find the bind/lambda versions less error-prone than writing the equivalent (small) for/while loop, but I tend to put the lambda expression that I really want to write in a comment. Doug

Darren Cook wrote:
Thanks for the reply. I can almost match that up to the parts in the while loop, except mention of "list" is missing. Should it be:
boost::bind(&Points::add, &list, boost::bind(&Info::key, boost::bind(&InfoContainer::get, &cs, _1))));
However, if Points is std::set<int>, then "add" should be "insert"
Sorry. Points is a class that can be thought of as a wrapper for std::set.
I
thought I could simplify the example by substituting, but of course I hadn't thought it through properly had I.
BTW, I was hoping I'd be able to do something like this: std::for_each(c->points.begin(),c->points.end(), list.add(cs.get( _1 )->key) );
But after some experiments with boost::lambda I see why this isn't possible. I think if just one call to bind is possible then it is worth it, but when bind is required 2 or more times, falling back to a while loop or a functor seems better. Is anyone else using a similar guideline?
Yes, this is a reasonable guideline, but you should also consider:
1. Adding InfoContainer::get_key as an alias for get(_1)->key
2. Using std::transform with a std::inserter, as Doug already mentioned. An
overcomplicated bind expression sometimes indicates that you're using the
wrong algorithm.
Since Points is not an STL container, std::inserter won't work but one
reasonable alternative would be
template
participants (3)
-
Darren Cook
-
Douglas Gregor
-
Peter Dimov