HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppModerate

Making C++11 range-based for loops a bit more useful

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
bitrangeloopsmoreforbasedusefulmaking

Problem

C++11 is great. Probably one of the most beautiful features (in my opinion) is the so-called range-based-for-loop.
Instead of

for ( std::size_t i(0); i < range.size(); ++i )
{
   // do something to range[i];
}


or

for ( Range::iterator it(range.begin()); it != range.end(); ++it )
{
   // do something to *it;
}


or simplified with C++11 auto:

for ( auto it(range.begin()); it != range.end(); ++it )
{
   // do something to *it;
}


we can say this:

for ( auto& entry : range )
{
   // do something with entry.
}


This is very expressive: We talk about the entry, rather than the i^th position in the range or the iterator pointing to an entry. However, this syntax lacks the ability of having subranges (e.g. ignoring the last entry). In a series of small helper structs / methods, I want to add this functionality in a clean way.

So much for my motivation ;-) Now, for the real deal.

In this post, I address conditions on the entries of range. Basically, the helper code should perform the equivalent of

for ( auto& entry : range )
{
   if ( condition(entry) )
   {
       // do something to entry.
   }
}


but without that level of indentation.

Here is the code for ConditionalRange:

```
template
ConditionalRange makeConditionalRange(Range& range, Runnable&& condition)
{
static_assert(std::is_same().begin())), bool>::value, "Condition must return a boolean value.");
return ConditionalRange(range, std::forward(condition));
}

template
struct ConditionalRange
{
public:
friend ConditionalRange makeConditionalRange<>(Range&, Runnable&&);
public:
using iterator_type = ConditionalIterator().begin()), Runnable>;
iterator_type begin() const
{
auto b = range.begin();
while ( b != range.end() && !condition(*b) )
{
++b;
}
return ConditionalIterator().begin()), Runnable>(b, range.end(), condition);
}
iterator_type end() const
{

Solution

You can use braces to return from your functions. That way, you won't have to repeat long and cumbersome types (DRY, as much as possible). For example, ConditionalRange::end:

iterator_type end() const
{
    return { range.end(), range.end(), condition };
}


Also, you probably want your function to accept C-style arrays too. Therefore, you should use std::begin and std::end instead of calling the member functions:

iterator_type end() const
{
    return { std::end(range), std::end(range), condition };
}


With that (everywhere in your code) and some trivial changes, your code will also work for C-style arrays (tested here and it works fine).

Naming your function

The name makeConditionalRange is quite long. The function you created already exists in other libraries and programming languages (Python comes to my mind) under the name filter. You should consider changing its name in order for many users to recognize the name at first glance (moreover, it will be shorter).

Code Snippets

iterator_type end() const
{
    return { range.end(), range.end(), condition };
}
iterator_type end() const
{
    return { std::end(range), std::end(range), condition };
}

Context

StackExchange Code Review Q#43308, answer score: 10

Revisions (0)

No revisions yet.