Elegant way to iterate in C ++

Say I have a vector of polygons, where each polygon contains a vector of points. I have to repeatedly iterate over all the points of the polygons in my code, I have to write the same code again and again:

for(std::vector<Polygon*>::const_iterator polygon = polygons.begin(); polygon != polygons.end(); polygon++) { for(std::vector<Point>::const_iterator point = (*polygon)->points.begin(); point != (*polygon)->points.end(); point++) { (*point).DoSomething(); } } 

I really feel that there is a lot of code for two simple iterations, and I feel that it clogs the code and interferes with readability.

Some parameters that I thought:

  • using #defines - but this will make it unportable (for use in other parts of the code). In addition, #defines are considered evil at this time;
  • iterating over vector-> size () - this doesn't seem to be the most elegant way;
  • calling a method with a function pointer - but in this case, the code that should be inside the loop will be far from the loop.

So what would be the cleanest and most elegant way to do this?

+7
source share
7 answers

In C ++ 11, using ranged-base for loops and auto keyword:

 for(const auto& polygon : polygons) { for(const auto& point : polygon->points) { point.DoSomething(); } } 
+24
source

If you cannot use C ++ 11, boost has a FOREACH macro that generates a lot of code, but greatly simplifies the code:

 BOOST_FOREACH(Polygon * polygon, polygons) { BOOST_FOREACH( Point & point, polygon->points ) { point.doSomething(); } } 
+9
source

The inner loop can be rewritten using the following algorithms:

 std::for_each( (*polygon)->points.begin(), (*polygon)->points.end(), &Point::DoSomething ); 

Mixing this with an outer loop is a bit more complicated:

 std::for_each( polygons.begin(), polygons.end(), []( Polygon* polygon ) { std::for_each( polygon->points.begin(), polygon->points.end(), &Point::DoSomething ); } ); 

If we had some kind of complex iterator, we could really express your intention, which is to do something for every point in every polygon. And a range library, such as Boost.Range, will allow you to avoid naming each container twice, given that you want to work with your entire range.

My ideal version of your code would look like this:

 for_each( flatten( polygons ), &Point::DoSomething ); 

were flatten return the representation of each Point in each Polygon , as if it were the only continuous range. Please note that this is something that can be done in simple C ++ 03, and all that we lack in Boost.Range to achieve this is the flatenning range, which is not difficult to implement in terms of the join range.

Otherwise, the for-loop along with auto will help you reduce the iteration pattern by throwing away the range and forgetting about complex types.

+5
source

If you cannot use C ++ 11, perhaps the typedef iterator type will be something shorter than

 typedef std::vector<Polygon*>::const_iterator PolyCit; for (PolyCit polygon = polygons.begin(); polygon != polygons.end(); polygon++) 
+5
source

Nevermind, this will not work for you, since you have a pointer to pointers at the top level, but I will support it because I think this is pretty cool.


My metaprogramming pattern is a little rusty, so there may be an easier way to do this, but:

 template<typename C, typename F, size_t depth> struct nested_for_each_helper { static void do_it(C& c, F& f) { for (auto& i : c) nested_for_each_helper<decltype(i),F,depth-1>::do_it(i,f); } }; template<typename C, typename F> struct nested_for_each_helper<C,F,0> { static void do_it(C& c, F& f) { f(c); } }; template<size_t depth, typename C, typename F> void nested_for_each(C& c, F& f) { nested_for_each_helper<C,F,depth>::do_it(c,f); } int main() { int n[3][3][3][3]; int i = 0; nested_for_each<4>(n,[&i](int& n) { n = i++; }); nested_for_each<4>(n,[](int n){ std::cout << n << ' '; }); } 

In your case, you can use it like this (you cannot):

 nested_for_each<2>(polygons, [](Point const& p) { p.DoSomething(); }); 
+1
source

You need an abstraction layer. Instead of dealing with a vector of polygons, write a class that controls this vector. The class then provides pairs of iterators to iterate over points. The code in the iterator knows and encapsulates these details.

+1
source

First of all, there is a simple static integer loop. A bit shorter than iterators.

 for( int i = 0 ; i < polygons.size() ; i++ ) { for( int j = 0 ; j < polygons[i]->points.size(); j++) { Point* p = polygons[i]->points[j] ; p->DoSomething(); } } 

If you do not like this and you do not have C ++ 11 available, you can write a function that accepts a functor (they were in C ++ 0x under std::tr1 , I suppose):

 void eachPoint( function<void (Point* p)> func ) { for( int i = 0 ; i < polygons.size() ; i++ ) { for( int j = 0 ; j < polygons[i]->points.size(); j++) { Point* p = polygons[i]->points[j] ; func(p); } } } 

Alternatively, a simple old macro:

 #define EACH_POLYGON( polyCollection ) for( int _polyI = 0 ; _polyI < polyCollection.size() ; _polyI++ ) \ for( int _ptNo = 0, Point* p=polyCollection[_polyI]->points[0] ; j < polyCollection[_polyI]->points.size() && (p=polyCollection[_polyI]->points[_ptNo]); _ptNo++) EACH_POLYGON( polyCollection ) { p->DoSomething(); } 
0
source

All Articles