tags:

views:

1481

answers:

7

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.

A: 

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 ->.

Tyler
+6  A: 

You would be looking for Boost::For_each - 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
}
Tom Leys
Thanks for the link; that's probably a good replacement for the original. The main difference is that it requires declaring the type of the loop variable, which is ok sometimes but problematic for hairy types.Your updated answer does not work -- it declares two variables. It will not compile.
sfink
True! You could always use a std::pair to store the two loop vars or use scope in your macro. I'll remove the offending code from my post(for(itr_type itr = begin, value_type val = itr->second; itr != end; ++itr, val = itr->second ))
Tom Leys
The pair doesn't work because I still need the plain variable declared as well. The scope doesn't work because any scope created in the macro is local to the macro not the body of the for(), or it requires a matching foreach_end macro to close off a scope. (Or a bare '}', but yuck!)
sfink
+1  A: 

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.

Porges
I think you may be right about the transform_iterator, but I'll have to play with it to be sure. (And yes, I'm using various bits of boost already, although I'm familiar with only a fraction of what it offers.)
sfink
+2  A: 

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.

archbishop
Perfect! But I don't understand the bit about making an extra object. var is a pointer. Did you have an earlier version that used a value? And var could be made a reference with yet another (innermost) loop: for(typeof(...) var = *ptr; ptr; ptr = NULL). Care to update with that?
sfink
+2  A: 

The STL transform function also does something similar.

The arguments are (in order):

  1. An input iterator designating the beginning of a container
  2. An input iterator designating the end of the container
  3. 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)
  4. 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.

Zachary Garrett
+1  A: 

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.

A: 
#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)

Assaf Lavie
That's why I mentioned "gcc" in the title -- it's a gcc extension. A very handy one. Standard C++ will eventually acquire the 'auto' keyword (or rather, new meaning for that keyword) that will take over most of what I use typeof for. But I'll update the question to better point out the gcc-ism.
sfink