The introduction of lambda functions in C++11 makes a new type of loop construct very convenient to write. I’ve been using this approach with increasing frequency in my own code, and it leads to clean solutions to all sorts of loop-related problems. This is basic textbook C++11, so those of you who already use C++11 features regularly will likely already be beyond familiar with all of this, but if you’re not, perhaps you’ll find this useful too.
First, let’s look at some of the common ways of writing loop-like constructs in imperative languages. There are three common conventions I’ve tended to encounter in imperative languages. The first is to explicitly increment an index counter, common in C, C++, and a host of other languages:
for(int i = 0; i < n; ++i) {
// code using i goes here
}
Another convention is to use an iterator instead of an explicit counter. Languages like C#, Python, and Icon implement this particularly nicely with support for generators at the language level, but you can implement a similar construct in almost any language:
for i in xrange(n):
# code using i goes here
C++ also contains conventions for writing iterator-based loops, and in C++11 language-level support was added with the syntax for(auto i : arr) { ... }
, making these loops quite easy to use, although I still find coding up the iterator itself to be a bit of a pain.
Another option is taken by Ruby, and is probably the most flexible. In effect, you create an anonymous function containing the code that you want run within the loop. This function is passed into another function which invokes it a number of times with the appropriate parameters:
n.times { |i|
# code using i goes here
}
Technically this sort of loop has been possible in C++ since the beginning, since it inherited the necessary capabilities from the function pointers supported in C. Nevertheless, the introduction of lambda functions in C++ makes this sort of loop much easier to write, so much so that they tend to actually simplify the resulting code.
Here’s an extremely simple example, which illustrates the idea even if it isn’t particularly useful just yet:
template<typename Func>
void loop(int n, const Func& f) {
for(int i = 0; i < n; ++i) {
f(i);
}
}
This construct can then be used like so:
loop(n, [&](int i) {
// code using i goes here
});
The nice thing about this approach is that it cleanly separates the code executed within the loop from the code determining how the loop itself is executed. For example, it’s almost trivial to write a version of loop
which executes the iterations in parallel:
template<typename Func>
void parallel_loop(int n, const Func& f) {
#pragma omp parallel for
for(int i = 0; i < n; ++i) {
f(i);
}
}
This parallel_loop
function is called in exactly the same way as the loop
function given previously, and comes with the added advantage that you can change the multithreading to something other than OpenMP without touching anything other than the parallel_loop
function itself. The simplicity and flexibility of this approach has led to its usage in a number of modern parallel processing libraries, such as the parallel_for
found in Intel’s Threading Building Blocks or the parallel_for_each
in Microsoft’s Parallel Patterns Library.
The flexibility of this way of writing loops makes it useful in situations more general than basic for-loops. For instance it’s a handy to time the execution of a block of code. The version below is the simplest form, but it’s easy to modify to run the function f
multiple times and collect statistics. Again, this modification involves no change to how time_in_seconds
is called (other that potentially dealing with a change in the return value type):
template<typename Func>
double time_in_seconds(const Func& f) {
std::chrono::time_point<std::chrono::system_clock> start, end;
start = std::chrono::system_clock::now();
f();
end = std::chrono::system_clock::now();
return (end-start).count();
}
Another easy example is a utility function to fill in a summed area table:
template<typename Arr, typename Func>
void calc_summed_area_table(
Arr& S,
const Func& f
) {
for(int j = 0; j < S.height(); ++j) {
for(int i = 0; i < S.width(); ++i) {
auto x = f(i,j);
if(i > 0 && j > 0) {
S(i,j) = x + S(i-1,j) + S(i,j-1) - S(i-1,j-1);
} else if(i > 0) {
S(i,j) = x + S(i-1,j);
} else if(j > 0) {
S(i,j) = x + S(i,j-1);
} else {
S(i,j) = x;
}
}
}
}
You probably get the idea by this point. There are tons of other cases where this pattern can be used. Not surprising since, after all, there are languages like Ruby which use something similar as their most common mechanism for writing loops. One drawback worth noting about the C++ case, however, is that you lose the ability to use the break
statement. You can fake it to a degree with a bit of effort, for instance by having the passed in function return an error code or the like, but it’s definitely not as simple as with normal for
-loops. This is the price to be paid for the added flexibility. Still, even lacking this feature it’s a surprising handy pattern to have at one’s disposal.