Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"foreach values" macro in gcc & cpp

Tags:

c++

gcc

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.

like image 278
sfink Avatar asked Sep 17 '08 00:09

sfink


3 Answers

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
}
like image 103
Tom Leys Avatar answered Oct 15 '22 16:10

Tom Leys


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.

like image 38
archbishop Avatar answered Oct 15 '22 16:10

archbishop


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.

like image 43
Zachary Garrett Avatar answered Oct 15 '22 16:10

Zachary Garrett