Sunday, April 17, 2011

Outrageous: for_each #define

When people think of C++, over-verbose is not usually the first thought. But it actually can really get so when dealing with the STL. Things like .begin() and .end() are really good at breaking your willing suspension of disbelief when you want to believe that C++ is a language that is there to help you :). The c++0x standard will improve c++ in many ways, but I fear it does not really enough new ways to save the problem of typing .begin() and .end().

Here is my problem. You have a set of pair<int, pair<int,int> > . That sort of STL abuse happens just about any time you are in a programming contest and wish to avoid having to make your own class with three elements and tie breaking comparisons. The pair and set templates are very useful when implementing some algorithms. The set template can function as a priority queue and also as a range tree if you know how to extract juice out of it. The thing is that it happens too frequently, that I need to iterate through the elements of a set of some complex data structure.

set<pair<int,pair<int,int> > > s;
for(set<pair<int,pair<int,int> > >::iterator q = s.begin(); q!=s.end(); q++) {
cout<< q->first <<", " << q->second.first << ", " << q->second.second<<endl;
}


If that did not induce you a headache, it is because you have been over exposed to c++. I love c++, so I consider it a good thing, but things can always be better.

The one million question is why does not c++0x seem to introduce anything close to Java's for(:) syntax, which allows you to iterate through containers very easily. I really, really dislike defines, but for some reason, c++ uses them as an excuse not to implement some features, because supposedly you can make them yourself by abusing macros. Well, in this case, I am starting to think that a macro is the cleanest solution. That is the reason I have begun typing it during topcoder matches, and I think I will add it to my template by default and do something to make a script remove it from my submission when it is not in use. The for_each macro I am using is as follows:

#define for_each(s,v) for(typeof((v).begin()) s=(v).begin(); s!=(v).end(); s++)


Most special thing is the use of the g++ extension typeof(). It may eventually get removed from g++, but that is probably going to coincide with the day c++0x auto keyword gets added. (auto q=v.begin() is even better).

The previous code becomes much clearer:

set<pair<int,pair<int,int> > > s;
for_each(q,s) {
cout<< q->first <<", " << q->second.first << ", " << q->second.second<<endl;
}


The benefit is that for_each should work with every STL container. But you would need a different macro if you want to, for example, iterate a vector in reverse order.

No comments :