52

I find boost::foreach very useful as it saves me a lot of writing. For example, let's say I want to print all the elements in a list:

std::list<int> numbers = { 1, 2, 3, 4 };
for (std::list<int>::iterator i = numbers.begin(); i != numbers.end(); ++i)
   cout << *i << " ";

boost::foreach makes the code above much simplier:

std::list<int> numbers = { 1, 2, 3, 4 };
BOOST_FOREACH (int i, numbers)
   cout << i << " ";

Much better! However I never figured out a way (if it's at all possible) to use it for std::maps. The documentation only has examples with types such as vector or string.

Andreas Bonini
  • 44,018
  • 30
  • 122
  • 156
  • 1
    This isn't exactly a duplicate, but see here: http://stackoverflow.com/questions/461507/how-to-use-boostforeach-with-a-boostptrmap/461908#461908 – Michael Kristofik Jan 20 '10 at 19:26

8 Answers8

88

You need to use:

typedef std::map<int, int> map_type;
map_type map = /* ... */;

BOOST_FOREACH(const map_type::value_type& myPair, map)
{
    // ...
}

The reason being that the macro expects two parameters. When you try to inline the pair definition, you introduce a second comma, making the macro three parameters instead. The preprocessor doesn't respect any C++ constructs, it only knows text.

So when you say BOOST_FOREACH(pair<int, int>, map), the preprocessor sees these three arguments for the macro:

1.pair<int
2. int>
3. map

Which is wrong. This is mentioned in the for-each documentation.

Andreas Bonini
  • 44,018
  • 30
  • 122
  • 156
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • Make that `pair`. – UncleBens Jan 20 '10 at 19:27
  • 1
    The last edit introduces some misinformation. There is no undefined behavior, since the last two examples won't compile. `std::map` protects its key itself: if you have `map` then the value type is `pair`. Note that it makes the key type const. – UncleBens Jan 20 '10 at 20:02
  • Can you edit your answer again? I accidentally rescinded my upvote and now it won't make me recast it unless you edit =p EDIT: oh, cool, I edited it myself and it worked :) +1 regiven! – Andreas Bonini Jan 20 '10 at 20:38
  • Note in C++11 you can also just do `BOOST_FOREACH(const auto& myPair, map)` – Claudiu Apr 12 '15 at 19:47
20

I use Boost's Range Ex library which implements some fancy range adaptors for iterating over map keys or values. For instance:

map<int, string> foo;
foo[3] = "three";
foo[7] = "seven";

BOOST_FOREACH(i, foo | map_keys)
   cout << i << "\n";


BOOST_FOREACH(str, foo | map_values)
   cout << str << "\n";
Manuel
  • 12,749
  • 1
  • 27
  • 35
3

Sure you can. The trick is, however, that a map iterator points to a pair of the key and value. It would look something like this:

typedef std::map<std::string, int> MapType;
MapType myMap;

// ... fill the map...

BOOST_FOREACH(MapType::value_type val, myMap)
{
    std::cout << val.first << ": " << val.second << std::endl;
}
Fred Larson
  • 60,987
  • 18
  • 112
  • 174
  • Well, I tried with `BOOST_FOREACH(int i, map)`, `BOOST_FOREACH(pair, map)`, etc. Can you post a working example? – Andreas Bonini Jan 20 '10 at 19:20
  • 3
    Someone could mention that `BOOST_FOREACH` is a *macro*, and therefore it can't properly deal with the comma in the pair template. This is the reason why everybody is suggesting a typedef. – UncleBens Jan 20 '10 at 19:29
  • @UncleBens: I think the typedef just makes it look a whole lot cleaner, even if the macro can handle the comma (not sure whether it can). – Fred Larson Jan 20 '10 at 19:30
  • 2
    Comma has only one meaning for the preprocessor - argument separator. It is not aware of templates and that a comma that falls between `<>` does not introduce another argument. – UncleBens Jan 20 '10 at 19:33
  • @UncleBens: Yes, it looks like you're right. But that wasn't my thought in using the typedef. I think that was the OP's problem all along, although I had no way to know that when I came up with the sample code. – Fred Larson Jan 20 '10 at 19:53
2

I didn't like the idea to be forced to add typedefs each time I wanted to use a foreach on a map. So here is my implementation based on the boost foreach code:

#ifndef MUNZEKONZA_FOREACH_IN_MAP 

#include <boost/preprocessor/cat.hpp>
#define MUNZEKONZA_FOREACH_IN_MAP_ID(x)  BOOST_PP_CAT(x, __LINE__)

namespace munzekonza {
namespace foreach_in_map_private {
inline bool set_false(bool& b) {
  b = false;
  return false;
}

}
}

#define MUNZEKONZA_FOREACH_IN_MAP(key, value, map)                            \
for(auto MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) = map.begin();      \
        MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end();)       \
for(bool MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true;       \
      MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) &&               \
      MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end();          \
      (MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue)) ?              \
        ((void)++MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)) :          \
        (void)0)                                                              \
  if( munzekonza::foreach_in_map_private::set_false(                          \
          MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue))) {} else    \
  for( key = MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)->first;         \
        !MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue);              \
        MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true)        \
  if( munzekonza::foreach_in_map_private::set_false(                          \
          MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue))) {} else    \
  for( value = MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)->second;      \
        !MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue);              \
        MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true)        

Then you can use it in your code: #define foreach_in_map MUNZEKONZA_FOREACH_IN_MAP

std::map<int, std::string> mymap;
mymap[0] = "oi";
mymap[1] = "noi";

std::map<int, std::string> newmap;

foreach_in_map(int key, const std::string& value, mymap) {
  newmap[key] = value;
}

ASSERT_EQ( newmap.size(), 2 );
ASSERT_EQ( newmap.count(0), 1 );
ASSERT_EQ( newmap.count(1), 1 );
ASSERT_EQ( newmap.at(0), "oi" );
ASSERT_EQ( newmap.at(1), "noi" );

You can also change the values: #define foreach_in_map MUNZEKONZA_FOREACH_IN_MAP

std::map<int, std::string> mymap;

mymap[0] = "oi";
mymap[1] = "noi";

std::map<int, std::string> newmap;

foreach_in_map(int key, std::string& value, mymap) {
  value = "voronoi" + boost::lexical_cast<std::string>(key);
}

ASSERT_EQ( mymap.size(), 2 );
ASSERT_EQ( mymap.count(0), 1 );
ASSERT_EQ( mymap.count(1), 1 );
ASSERT_EQ( mymap.at(0), "voronoi0" );
ASSERT_EQ( mymap.at(1), "voronoi1" );
marko.ristin
  • 643
  • 8
  • 6
2

It's possible, but it's not really the best way to do things (as I've mentioned a few times before, for_each almost never is, and BOOST_FOREACH is only marginally better). For your first example, I think you'd be better off with:

std::copy(numbers.begin(), numbers.end(), 
          std::ostream_iterator<int>(std::cout, " "));

It works pretty similarly with a map, except that you have to define operator<< for it, since there isn't one already defined:

typedef map<std::string, int>::value_type vt;

std::ostream &operator<<(std::ostream &os, vt &v) { 
    return os << v.first << ": " << v.second;
}

...and once again, std::copy does the job quite nicely:

std::copy(mymap.begin(), mymap.end(), 
          std::ostream_iterator<vt>(std::cout, "\n"));
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 1
    +1. I agree with you, Jerry, although some might argue that having to define the operator (oops, you have a typo there!) is more trouble than the BOOST_FOREACH. – Fred Larson Jan 20 '10 at 19:51
  • @Fred: They can argue that, and to an extremely minimal extent, it's even true. Then again, doing the job right often is a bit more work (at least up-front) than just hacking out something that sort of works. – Jerry Coffin Jan 20 '10 at 20:05
2

Typedefing a map pair is confusing. The most simplest way to iterate a map is with a tuple(just like in python):

std::map<int, int> mymap;
int key, value;
BOOST_FOREACH(boost::tie(key, value), mymap)
{
    ...
}

And don't worry, those commas won't confuse the preprocessor because I placed parenthesis around them.

Paul Fultz II
  • 17,682
  • 13
  • 62
  • 59
  • This has a drawback of copying the value of the map. It might be expensive if it is not a primitive. – balki Dec 17 '12 at 17:56
1

Yes:

typedef std::map<std::string,int>    MyMap;

MyMap    myMap;

BOOST_FOREACH(MyMap::value_type loop, myMap)
{ 
       // Stuff
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
0

In C++0x you can more easily do:

map<int, string> entries;
/* Fill entries */

foreach(auto i, entries)
   cout << boost::format("%d = %s\n") % i.first % i.second;
Andreas Bonini
  • 44,018
  • 30
  • 122
  • 156