Monday, July 2, 2012

Python style range loops in C++

Python is a nice scripting language which has some really flexible characteristics. One thing I like about it is the integer range for-loops that can be achieved using the built-in function range:
for i in range(10):
    # Insert clever statement below 
    print i 
Doing that same thing in C++ would require some for loop like the one below:
for(size_t i(0); i < 10; ++i)
    std::cout << i << std::endl;
Which is larger and less clearer(well, not that much :D). So I created a simple wrapper to achieve the same thing in C++, using C++11's range-based for loops. The wrapper can be found here.

In order to use ranges, a function named range should be used, which contains these overloads:
// Returns a range_wrapper<T> containing the range [start, end)
template<class T>
range_wrapper<T> range(const T &start, const T &end) {
    return {start, end};
}

// Returns a range_wrapper<T> containing the range [T(), end)
template<class T>
range_wrapper<T> range(const T &end) {
    return {T(), end};
}

range_wrapper<> is a simple wrapper that defines begin() and end(), both return a range_iterator, the former one pointing to the beginning of the range and the latter to the end. The range_iterator<> class template is just a wrapper over the template parameter, and defines all of the forward iterator required member functions/typedefs. The prefix/suffix increment operators apply the same operator on the wrapped object, while the dereference operator returns it.

Since range-based for loops require the iterated sequence to define begin() and end() or that the global std::begin()/end() functions are defined for the given type, using the range_wrapper<> class template in these for loops is perfectly valid.

Using this wrapper, that code can be reduced to this:
// Prints numbers in range [0, 10) 
for(auto item : range(10))
    std::cout << item << std::endl;

Using the first overload, which takes the start and end of the range, we can indicate the starting number.
// Prints numbers in range [5, 15) 
for(auto item : range(5, 15))
    std::cout << item << std::endl;
Note that range is a template function, so it could be adapted to perform range iteration through other types(std::string comes to my mind right now).

This same thing can be achieved using boost::irange, but hey, I was bored and wanted to implement it myself.

3 comments: