
As documented in the manual ("Pitfalls"), BOOST_FOREACH is a little awkward to use with associative containers. I've used the following in the past to simplify my own code, and wondered if it was suitable to add to the Boost Foreach library proper. std::map<int, int> my_map; BOOST_FOREACH_PAIR(int key, int value, my_map) std::cout << key << " : " << value << "\n"; The BOOST_FOREACH_PAIR macro is a clone of the BOOST_FOREACH macro with an extra for loop in it to assign both first and second from each pair in the map. If either is not wanted, ignore from Boost.Tuples can be used: std::vector<int> values; BOOST_FOREACH(boost::tuples::ignore, int value, my_map) values.push_back(value); References can be used just as with BOOST_FOREACH, to create a modifying loop: BOOST_FOREACH_PAIR(int key, int& value, my_map) value += key; There are a few alternatives to my solution that I have seen: (1) Predeclare a pair and use it each time around the loop: std::pair<int, int> kv; BOOST_FOREACH(kv, my_map) std::cout << kv.first << " : " << kv.second << "\n"; This adds overhead from copying pairs, and does not give meaningful names to the two halves of the pair. It also has no simple extension to a mutating loop that I am aware of. (2) Typedef the value type of the map before the loop, and use .first and .second: typedef std::pair<const int, int> KeyValue; BOOST_FOREACH(KeyValue const& kv, my_map) std::cout << kv.first << " : " << kv.second << "\n"; This removes the copying overhead, and is easy to change to a mutating loop, but still does not meaningfully name the key and value (3) Use Boost.Tuple's tie to turn the single assignment of BOOST_FOREACH into a double assignment: int key, value; BOOST_FOREACH(boost::tie(key, value), my_map) std::cout << key << " : " << value << "\n" This gives meaningful names, but has copying overhead (albeit not in this simple example). It also has no simple extension to a mutating loop that I am aware of. The key objection to all of these alternatives, from my point of view, is simply that they are unnecessarily complicated. The new macro is much cleaner. Implementation of BOOST_FOREACH_PAIR is simple: turn the innermost assignment loop of BOOST_FOREACH into a pair of loops, one assigning key, one value. Cheers, Chris Purcell

Chris Purcell <chris.purcell.39 <at> gmail.com> writes:
As documented in the manual ("Pitfalls"), BOOST_FOREACH is a little awkward to use with associative containers. I've used the following in the past to simplify my own code, and wondered if it was suitable to add to the Boost Foreach library proper.
std::map<int, int> my_map; BOOST_FOREACH_PAIR(int key, int value, my_map) std::cout << key << " : " << value << "\n";
Can you provide a link to the code? I'd like to test it. Markus

2009/9/14 Markus Werle <numerical.simulation@web.de>:
Chris Purcell <chris.purcell.39 <at> gmail.com> writes:
std::map<int, int> my_map; BOOST_FOREACH_PAIR(int key, int value, my_map) std::cout << key << " : " << value << "\n";
Can you provide a link to the code? I'd like to test it.
I haven't got a web page to put it, but it's pretty small, so I've put it inline sans newlines. It's alpha-quality only. #define BOOST_FOREACH_PAIR(KEY, VALUE, COL) BOOST_FOREACH_PREAMBLE() if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_col) = BOOST_FOREACH_CONTAIN(COL)) {} else if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_cur) = BOOST_FOREACH_BEGIN(COL)) {} else if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_end) = BOOST_FOREACH_END(COL)) {} else for (bool BOOST_FOREACH_ID(_foreach_continue) = true, BOOST_FOREACH_ID(_foreach_key_loop) = true; BOOST_FOREACH_ID(_foreach_continue) && !BOOST_FOREACH_DONE(COL); BOOST_FOREACH_ID(_foreach_continue) ? BOOST_FOREACH_NEXT(COL) : (void)0) if (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_continue))) {} else if (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_key_loop))) {} else for (KEY = BOOST_FOREACH_DEREF(COL).first; !BOOST_FOREACH_ID(_foreach_key_loop); BOOST_FOREACH_ID(_foreach_key_loop) = true) for (VALUE = BOOST_FOREACH_DEREF(COL).second; !BOOST_FOREACH_ID(_foreach_continue); BOOST_FOREACH_ID(_foreach_continue) = true) I don't know if there are any changes to the library on subversion trunk; the above works with Boost 1.40.0. Any line breaks have been added en-route. Cheers, Chris

Is there any performance difference between BOOST_FOREACH_PAIR(K const &key, V &val, map) { } and BOOST_FOREACH(Map::reference ref, map) { } ? On Tue, Sep 15, 2009 at 7:04 AM, Chris Purcell <chris.purcell.39@gmail.com>wrote:
2009/9/14 Markus Werle <numerical.simulation@web.de>:
Chris Purcell <chris.purcell.39 <at> gmail.com> writes:
std::map<int, int> my_map; BOOST_FOREACH_PAIR(int key, int value, my_map) std::cout << key << " : " << value << "\n";
Can you provide a link to the code? I'd like to test it.
I haven't got a web page to put it, but it's pretty small, so I've put it inline sans newlines. It's alpha-quality only.
#define BOOST_FOREACH_PAIR(KEY, VALUE, COL) BOOST_FOREACH_PREAMBLE() if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_col) = BOOST_FOREACH_CONTAIN(COL)) {} else if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_cur) = BOOST_FOREACH_BEGIN(COL)) {} else if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_end) = BOOST_FOREACH_END(COL)) {} else for (bool BOOST_FOREACH_ID(_foreach_continue) = true, BOOST_FOREACH_ID(_foreach_key_loop) = true; BOOST_FOREACH_ID(_foreach_continue) && !BOOST_FOREACH_DONE(COL); BOOST_FOREACH_ID(_foreach_continue) ? BOOST_FOREACH_NEXT(COL) : (void)0) if (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_continue))) {} else if (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_key_loop))) {} else for (KEY = BOOST_FOREACH_DEREF(COL).first; !BOOST_FOREACH_ID(_foreach_key_loop); BOOST_FOREACH_ID(_foreach_key_loop) = true) for (VALUE = BOOST_FOREACH_DEREF(COL).second; !BOOST_FOREACH_ID(_foreach_continue); BOOST_FOREACH_ID(_foreach_continue) = true)
I don't know if there are any changes to the library on subversion trunk; the above works with Boost 1.40.0. Any line breaks have been added en-route.
Cheers, Chris _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Christian Schladetsch wrote:
Is there any performance difference between
BOOST_FOREACH_PAIR(K const &key, V &val, map) { }
and
BOOST_FOREACH(Map::reference ref, map) { }
I haven't tested this specific case but in previous discussions and tests on this subject, I found these extra for-loops of fixed "length" 1 hidden in the macro expansion were optimized well by popular compilers, so the hit may be negligible. Pete

Compare: // (1) my typical use, where Map is a typedef or a template parameter foreach (Map::reference val, map) { action(val.first, val.second); } // (2) use based on typedef'd container foreach_pair (Map::key_type const &key, Map::referent_type &val, map) { action(key, val); } // (3) use references foreach_pair (Key const &key, Value &val, map) { action(key, val); } // (4) copying without a typedef foreach_pair (Key key, Val val, map) { action(key, val); } A hidden cost of (3) and (4) is the extra work required when refactoring. On Tue, Sep 15, 2009 at 10:22 AM, Pete Bartlett <pete@pcbartlett.com> wrote:
Christian Schladetsch wrote:
Is there any performance difference between
BOOST_FOREACH_PAIR(K const &key, V &val, map) { }
and
BOOST_FOREACH(Map::reference ref, map) { }
I haven't tested this specific case but in previous discussions and tests on this subject, I found these extra for-loops of fixed "length" 1 hidden in the macro expansion were optimized well by popular compilers, so the hit may be negligible.
Pete
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Chris Purcell wrote:
As documented in the manual ("Pitfalls"), BOOST_FOREACH is a little awkward to use with associative containers. [...]
There are a few alternatives to my solution that I have seen: [...]
Alternatives you didn't mention: 1) Use TheContainer::value_type instead of std::pair<foo, bar>. 2) Use type deduction to avoid having to provide the type of the elements altogether using auto or BOOST_AUTO. I think your BOOST_FOREACH_PAIR is interesting, but maybe it could be generalized to tuples?

Mathias Gaunard wrote:
Chris Purcell wrote:
As documented in the manual ("Pitfalls"), BOOST_FOREACH is a little awkward to use with associative containers. [...]
There are a few alternatives to my solution that I have seen: [...]
Alternatives you didn't mention:
1) Use TheContainer::value_type instead of std::pair<foo, bar>.
Problem is that TheContainer usually contains commas (e.g. std::map<..,..> ) so you are again looking at a typedef.
2) Use type deduction to avoid having to provide the type of the elements altogether using auto or BOOST_AUTO.
Can BOOST_AUTO be used with BOOST_FOREACH? From memory I thought it couldn't...
I think your BOOST_FOREACH_PAIR is interesting, but maybe it could be generalized to tuples?
It could. Pitfalls to avoid if possible are - ugly syntax of one macro per arity - parenthesis soup so common to PP-programming. I think maybe BOOST_FOREACH_TUPLE( (Arg1T)(arg1) .... (ArgNT)(argn) , range ) could be made to work.

Pete Bartlett wrote:
2) Use type deduction to avoid having to provide the type of the elements altogether using auto or BOOST_AUTO.
Can BOOST_AUTO be used with BOOST_FOREACH? From memory I thought it couldn't...
No, it cannot. You would need to use a BOOST_FOREACH_AUTO. I have one that I find quite practical to use.

Sorry I've taken a little while to respond, I don't really have the energy to code during the week. 2009/9/14 Pete Bartlett <pete@pcbartlett.com>:
Mathias Gaunard wrote: Chris Purcell wrote:
As documented in the manual ("Pitfalls"), BOOST_FOREACH is a little awkward to use with associative containers.
I think your BOOST_FOREACH_PAIR is interesting, but maybe it could be generalized to tuples?
It could. Pitfalls to avoid if possible are - ugly syntax of one macro per arity - parenthesis soup so common to PP-programming.
I think maybe
BOOST_FOREACH_TUPLE( (Arg1T)(arg1) .... (ArgNT)(argn) , range )
could be made to work.
This is a great idea. After a pointer in the right direction from Dan Marsden, I've generalized the macro to Boost.Fusion-compatible types. The syntax is as follows: std::map<int, int> my_map; BOOST_FOREACH_FIELD((int key)(int value), my_map) std::cout << key << " : " << value << "\n"; std::vector<boost::fusion::vector<int, std::string, int> > my_complex_type; BOOST_FOREACH_FIELD((int i)(std::string const& j)(int& k), my_complex_type) std::cout << "line: " << i << ", " << j << ", " << ++k; I think this is probably the least number of brackets possible without a macro per permutation. To get it working, I need a Boost.Preprocessor header and two Boost.Fusion headers: #include <boost/preprocessor/seq/for_each_i.hpp> #include <boost/fusion/include/at_c.hpp> #include <boost/fusion/include/std_pair.hpp> I need a single macro to do per-field assignment: #define BOOST_FOREACH_ASSIGN_VAR(R, ROW, I, VAR) for (VAR = boost::fusion::at_c<I>(ROW); !BOOST_FOREACH_ID(_foreach_leave_outerloop); BOOST_FOREACH_ID(_foreach_leave_outerloop) = true) And now the macro is once again a small modification of the existing BOOST_FOREACH macro: #define BOOST_FOREACH_FIELD(VARS, COL) BOOST_FOREACH_PREAMBLE() if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_col) = BOOST_FOREACH_CONTAIN(COL)) {} else if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_cur) = BOOST_FOREACH_BEGIN(COL)) {} else if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_end) = BOOST_FOREACH_END(COL)) {} else for (bool BOOST_FOREACH_ID(_foreach_continue) = true, BOOST_FOREACH_ID(_foreach_leave_outerloop) = true; BOOST_FOREACH_ID(_foreach_continue) && !BOOST_FOREACH_DONE(COL); BOOST_FOREACH_ID(_foreach_continue) ? BOOST_FOREACH_NEXT(COL) : (void)0) if (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_continue))) {} else if (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_leave_outerloop))) {} else BOOST_PP_SEQ_FOR_EACH_I(BOOST_FOREACH_ASSIGN_VAR, BOOST_FOREACH_DEREF(COL), VARS) for (; !BOOST_FOREACH_ID(_foreach_continue); BOOST_FOREACH_ID(_foreach_continue) = true) Once again, this is for the 1.40.0 release of boost, and any line breaks in the #defines have been added en-route. Cheers, Chris

Chris Purcell wrote:
After a pointer in the right direction from Dan Marsden, I've generalized the macro to Boost.Fusion-compatible types. The syntax is as follows:
std::map<int, int> my_map; BOOST_FOREACH_FIELD((int key)(int value), my_map) std::cout << key << " : " << value << "\n";
std::vector<boost::fusion::vector<int, std::string, int> > my_complex_type; BOOST_FOREACH_FIELD((int i)(std::string const& j)(int& k), my_complex_type) std::cout << "line: " << i << ", " << j << ", " << ++k; <snip>
Huh, interesting. Please open a feature request ticket at svn.boost.org, and I'll consider adding this. -- Eric Niebler BoostPro Computing http://www.boostpro.com

2009/9/20 Eric Niebler <eric@boostpro.com>:
Huh, interesting. Please open a feature request ticket at svn.boost.org, and I'll consider adding this.
https://svn.boost.org/trac/boost/ticket/3469 Cheers, Chris Purcell
participants (6)
-
Chris Purcell
-
Christian Schladetsch
-
Eric Niebler
-
Markus Werle
-
Mathias Gaunard
-
Pete Bartlett