Jonathan Boccara's blog

How to Flatten Out A Nested Switch Statement

Published June 27, 2017 - 1 Comment

With my team we’ve recently come across an annoying switch nested in another switch statement, and I want to show a solution for flattening out this sort of structure.

Flatten switch case nested collapse

Motivation

Let’s consider two enums representing the size and color of a shirt. While I don’t work in the clothing industry, using a simple example by stripping away all the domain specifics allows to focus on the C++ technique here.

The shirts come in two colors and three sizes:

We do a specific treatment for each of the combination of color and size of a shirt. Expressing this with switches gives something like this:

Several things are damaging the expressiveness of this piece of code:

  • it’s lengthy but without containing a lot of information,
  • the associated colors and sizes are far apart from one another: for example the case Size::Large within the case Color::Red is closer to the case Color::Blue in terms of lines of code than from the case Color::Red to which it belongs.
  • this design doesn’t scale: imagine that a third enum was involved. The code would then become even harder to read.

To make this code more expressive, I’m going to show how to flatten the double switch into a single one.

Collapsing a switch

Here’s an easy way to do this: creating an new enum that represents all the combinations of the other enums, and use it in the switch statement.

Let’s do it manually once, and then write a generic code to do it for us.

Here is the enum representing the combinations:

The ugly double switch can be encapsulated into a function that does the mapping between the original enum and this new one:

And then we can do a single switch statement on the combination of values. The key for this to work is that the combineEnums function is constexpr, so its return value can be put into a switch statement:

You’ll note that a constexpr function can throw exceptions. While this seems strange at first, it’s logical because a constexpr function can also be called at runtime. And if it ever tries to throw at compile time, the program doesn’t compile. All this is very well explained in Dietmar Kühl’s Constant Fun talk at CppCon on constexpr.

Although the switch statement has been flattened out, there is a lot of code that could be automated here.

Combining the enums automatically

Prerequisite: The generic solution I propose is based on one prerequisite: that the enums have all an extra last element with a consistent name, say “End_”, and that its value is not customized (as in End_ = 42). We could choose any other name, but I like “End_” because it has the same semantics of “one after the last one” as in the STL. I need this to manipulate the enums together (if you can think of a way to fill the same need without the End_, the comments section is all yours).

So our two enums become:

The idea is now to give a unique value for each association of enum values. The most compact (and, in my opinion, the most natural) way to do this is by using the following formula:

combinedValue = (Color value) + (numbers of possible color values) * (Size value)

One way to view this formula is that for each value of the Size enum, there are as many values as there are possible Colors.

The formula manipulates enum values like numeric values. To do this, we throw away all the type safety brought by the enum classes:

This code snippet is supposed to make you feel very uneasy. But worry not, we’ll put all the type safety back in just a moment.

And here is how to get the number of possible values of an enum:

Hence the need for End_.

And here is the implementation of the formula:

which is still constexpr, to be able to fit into the cases of a switch statement.

Putting type safety back

Now have a look at this example of usage. See anything wrong?

There is a bug in the third case:

This could happen because I’ve thrown away type safety a little earlier. I really asked for this one.

A way to put type safety back in place is to add typing to the combineEnums function. To do this I’m going to:

  • transform the combineEnums function into a function object
  • move the template types corresponding to the enums over to the object rather than the function
  • use the same object instance in the whole switch statement.

So for a start, here is the function’s code packed into an object:

Then we construct the object with the right enum types before the switch statement:

and using the wrong enum in a case becomes a compilation error:

Safety’s back.

Going generic

EDIT: I thought that a simple recursion on variadic templates was enough to make this technique work on any number of enums. But as reddit user /u/minirop pointed out with a revealing example, I was wrong. The presented implementation only works for two enums. Therefore I’ll leave this section empty and will re-work the implementation to make it more generic. This will be the topic of a later post.

Stepping back

I’ve found this technique efficient to flatten out switch statements and to bring associated values together in the cases. This really improves code readability.

However, it may not be the right choice for every situation (what is, really). For instance this technique doesn’t let you have a case covering a given value of Color for all possible values of Size.

Also, switches on enums often raise the question of hidden polymorphism: wouldn’t these enums be better off refactored into types? In this case the need to route on several types draws the code into multiple dispatch, which C++ doesn’t support natively. One solution for this is the (much criticized) visitor pattern.

But enums are there for a reason. And when switches start nesting into one another, this technique for ironing them out comes in handy.

Liked it ? Share this post ! Facebooktwittergoogle_plus    Don't want to miss out ? Follow:   twitterrss

Receive regular updates to make your code more expressive.