Question
I have a 'foreach' macro I use frequently in C++ that works for most STL containers:
#define foreach(var, container) \
for(typeof((container).begin()) var = (container).begin(); \
var != (container).end(); \
++var)
(Note that 'typeof' is a gcc extension.) It is used like this:
std::vector< Blorgus > blorgi = ...;
foreach(blorgus, blorgi) {
blorgus->draw();
}
I would like to make something similar that iterates over a map's values. Call it "foreach_value", perhaps. So instead of writing
foreach(pair, mymap) {
pair->second->foo();
}
I would write
foreach_value(v, mymap) {
v.foo();
}
I can't come up with a macro that will do this, because it requires declaring two variables: the iterator and the value variable ('v', above). I don't know how to do that in the initializer of a for loop, even using gcc extensions. I could declare it just before the foreach_value call, but then it will conflict with other instances of the foreach_value macro in the same scope. If I could suffix the current line number to the iterator variable name, it would work, but I don't know how to do that.
Solution
You can do this using two loops. The first declares the iterator, with a name which is a function of the container variable (and you can make this uglier if you're worried about conflicts with your own code). The second declares the value variable.
#define ci(container) container ## iter
#define foreach_value(var, container) \
for (typeof((container).begin()) ci(container) = container.begin(); \
ci(container) != container.end(); ) \
for (typeof(ci(container)->second)* var = &ci(container)->second; \
ci(container) != container.end(); \
(++ci(container) != container.end()) ? \
(var = &ci(container)->second) : var)
By using the same loop termination condition, the outer loop only happens once (and if you're lucky, gets optimized away). Also, you avoid calling ->second on the iterator if the map is empty. That's the same reason for the ternary operator in the increment of the inner loop; at the end, we just leave var at the last value, since it won't be referenced again.
You could inline ci(container), but I think it makes the macro more readable.
OTHER TIPS
You would be looking for BOOST_FOREACH - they have done all the work for you already!
If you do want to roll your own, you can declare a block anywhere in C++, which resolves your scope issue with your intermediate storage of itr->second ...
// Valid C++ code (which does nothing useful)
{
int a = 21; // Which could be storage of your value type
}
// a out of scope here
{
int a = 32; // Does not conflict with a above
}
The STL transform function also does something similar.
The arguments are (in order):
- An input iterator designating the beginning of a container
- An input iterator designating the end of the container
- An output iterator defining where to put the output (for an in-place transform, similar to for-each, just pass the the input iterator in #1)
- A unary function (function object) to perform on each element
For a very simple example, you could capitalize each character in a string by:
#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>
int main(int argc, char* argv[]) {
std::string s("my lowercase string");
std::transform(s.begin(), s.end(), s.begin(), toupper);
std::cout << s << std::endl; // "MY LOWERCASE STRING"
}
Alternatively there is also the accumulate function, which allows some values to be retained between calls to the function object. accumulate does not modify the data in the input container as is the case with transform.
Have you thought of using the Boost libraries? They have a foreach
macro implemented which is probably more robust than anything you'll write... and there is also transform_iterator
which would seem to be able to be used to do the second-extraction part of what you want.
Unfortunately I can't tell you exactly how to use it because I don't know enough C++ :) This Google search turns up some promising answers: comp.lang.c++.moderated, Boost transform_iterator use case.
Boost::For_each is by far your best bet. The nifty thing is that what they actually give you is the macro BOOST_FOREACH() which you can then wrap and #define to whatever you would really like to call it in your code. Most everyone will opt for the good old "foreach", but other shops may have different coding standards, so this fits with that mindset. Boost also has lots of other goodies for C++ developers! Well worth using.
I created a little Foreach.h helper with a few variants of foreach() including both ones operating on the local variables and on pointers, with also an extra version secured against deleting elements from within loop. So the code that uses my macros looks nice and cozy like this:
#include <cstdio>
#include <vector>
#include "foreach.h"
int main()
{
// make int vector and fill it
vector<int> k;
for (int i=0; i<10; ++i) k.push_back(i);
// show what the upper loop filled
foreach_ (it, k) printf("%i ",(*it));
printf("\n");
// show all of the data, but get rid of 4
// http://en.wikipedia.org/wiki/Tetraphobia :)
foreachdel_ (it, k)
{
if (*it == 4) it=k.erase(it);
printf("%i ",(*it));
}
printf("\n");
return 0;
}
output:
0 1 2 3 4 5 6 7 8 9
0 1 2 3 5 6 7 8 9
My Foreach.h provides following macros:
- foreach() - regular foreach for pointers
- foreach_() - regular foreach for local variables
- foreachdel() - foreach version with checks for deletion within loop, pointer version
- foreachdel_() - foreach version with checks for deletion within loop, local variable version
They sure do work for me, I hope they will also make your life a bit easier :)
There are two parts to this question. You need to somehow (1) generate an iterator (or rather, an iterable sequence) over you map's values (not keys), and (2) use a macro to do the iteration without a lot of boilerplate.
The cleanest solution is to use a Boost Range Adaptor for part (1) and Boost Foreach for part (2). You don't need to write the macro or implement the iterator yourself.
#include <map>
#include <string>
#include <boost/range/adaptor/map.hpp>
#include <boost/foreach.hpp>
int main()
{
// Sample data
std::map<int, std::string> myMap ;
myMap[0] = "Zero" ;
myMap[10] = "Ten" ;
myMap[20] = "Twenty" ;
// Loop over map values
BOOST_FOREACH( std::string text, myMap | boost::adaptors::map_values )
{
std::cout << text << " " ;
}
}
// Output:
// Zero Ten Twenty
You could define a template class that takes the type of mymap as a template parameter, and acts like an iterator over the values by overloading * and ->.
#define foreach(var, container) for (typeof((container).begin()) var = (container).begin(); var != (container).end(); ++var)
There's no typeof in C++... how is this compiling for you? (it's certainly not portable)
I implemented my own foreach_value
based on the Boost
foreach
code:
#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_VALUE(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( 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)
For example, you can use it in your code like this:
#define MUNZEKONZA_FOREACH_VALUE foreach_value
std::map<int, std::string> mymap;
// populate the map ...
foreach_value( const std::string& value, mymap ) {
// do something with value
}
// change value
foreach_value( std::string& value, mymap ) {
value = "hey";
}
#define zforeach(var, container) for(auto var = (container).begin(); var != (container).end(); ++var)
there is no typeof() so you can use this:
decltype((container).begin()) var
decltype(container)::iterator var