Serenity C++ patterns: IterationDecision

This post describes the C++ IterationDecision pattern used to implement flow control during callback-based iteration in the Serenity Operating System.


When iterating over something in C++, your code might look something like this:

for (auto& entry : m_entries) {
    if (entry.is_irrelevant())
        continue;
    if (entry.type() == Entry::Type::Cool) {
        if (do_stuff(entry))
            break;
    }
}

In the above example, we’re using continue to skip over some entries, and break to stop iterating early instead of visiting any more entries.

Okay, so..

The Serenity codebase makes frequent use of custom for_each_foo() iteration helpers that take a callback parameter and invoke it for each iteration step.

Such a helper might be implemented like this:

template<typename Callback>
void for_each_entry_of_type(Type type, Callback callback)
{
    for (auto& entry : m_entries) {
        if (entry.is_irrelevant())
            continue;
        if (entry.type() != type)
            continue;
        if (callback(entry) == IterationDecision::Break)
            break;
    }
}

Since the real “body” of the loop now happens in the callback function, we can’t use the continue and break statements to control the loop in for_each_entry. The only way we can communicate with that loop is via a return value from the callback.

So that’s exactly what we do! In <AK/IterationDecision.h> we have this enum:

enum class IterationDecision { Continue, Break };

When you call one of those for_each_foo() functions, you just need to make the callback function inform the loop what to do next by using return statements instead of continue or break:

for_each_entry_of_type(Entry::Type::Cool, [](auto& entry) {
    if (do_stuff_with(entry))
        return IterationDecision::Break;
    return IterationDecision::Continue;
}

This pattern can sometimes be a little more verbose than “native” loops, but it allows us to encapsulate iteration logic, which means that classes can expose public interfaces for iteration without having to show you the data structures inside.

Until next time!

Written on June 7, 2019