Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BOOST_FOREACH & templates without typedef

Tags:

c++

boost

When I work with BOOST_FOREACH, there isn't a problem with simple templates as vector. But when I try to iterate through map > for example I need to typedef the element type.

Is there any workaround?

like image 896
Anton Kazennikov Avatar asked Nov 11 '09 07:11

Anton Kazennikov


4 Answers

There is a problem because it is a macro, and therefore cannot handle types containing commas (preprocessor doesn't know about templates).

You can also declare the variable before the loop, see documentation.

std::map<int, double> my_map;

//1)
typedef std::pair<int, double> MyPair;
BOOST_FOREACH(MyPair p, my_map) { ... }

//2)
std::pair<int, double> p;
BOOST_FOREACH(p, my_map) { ... }

Edit:

There is a further complication with std::map in particular: the value_type is not std::pair<Key, Value>, but std::pair<const Key, Value>.

Hence, if you go with the typedef, a more proper way (and the only way if you want to use a reference in the foreach loop) is to use

typedef std::pair<const int, double> MyPair;
//or
typedef std::map<int, double>::value_type MyPair;

BOOST_FOREACH(MyPair& ref, my_map) { ... }

However, that won't work if you want to use a variable declared before the loop, since you can't assign to a std::pair<const int, double> instance later (can't assign to the const field), in which case you can only use pair<int, double> as boost's manual shows.

like image 113
UncleBens Avatar answered Nov 20 '22 07:11

UncleBens


If you need to iterate over a map, the easiest way is to use tuples, since getting the correct type in order to typedef, is troublesome. Here is how you can use tuples:

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

Just a note, the commas will work here because parenthesis are placed around key and value. The preprocessor only understands commas and parenthesis(and c99 requires it to understand quotation marks also). So, it can't parse the <> in std::pair<int, double>. However, we can use parenthesis to help the preprocessor. For example, if we have a a function that returns a container that is called like this:

BOOST_FOREACH(int i, foo<int, int>()) { ... } //This won't compile

So, we can place parenthesis around an expression and it will help the preprocessor:

BOOST_FOREACH(int i, (foo<int, int>())) { ... } //This will compile

However, in the case of a map, we can't place parenthesis around the declaration(because its not an expression). So this won't work:

BOOST_FOREACH((std::pair<int, double> p), my_map) { ... } //This won't work

Because it will get transformed into something like this (std::pair<int, double> p) = *it, and that, of course, is incorrect C++. But using a tie will work:

BOOST_FOREACH(tie(key, value), my_map) { ... } //This will work

We just need to declare key and value outside of the loop(as shown above). Plus, it can make the loop have more meaningful names. You can write key instead of p.first, and value instead of p.second.

like image 9
Paul Fultz II Avatar answered Nov 20 '22 06:11

Paul Fultz II


BOOST_FOREACH_PAIR is another option that works well in our experience:

http://lists.boost.org/Archives/boost/2009/09/156345.php

http://lists.boost.org/Archives/boost/2009/09/156366.php

like image 6
dtw Avatar answered Nov 20 '22 07:11

dtw


This can be as simple as this:

BOOST_FOREACH(auto& p, my_map) { ... }

Using the C++11 standard, auto is like var in C#, it will do

BOOST_FOREACH(std::pair<int, double>& p, my_map) { ... }
like image 2
Sylvain Rochette Avatar answered Nov 20 '22 06:11

Sylvain Rochette