Author: Chloé Lourseyre
Editor: Peter Fordham
I learned this syntax during a talk at the CppCon 2021 given by Herb Sutter, Extending and Simplifying C++: Thoughts on Pattern Matching using `is` and `as` – Herb Sutter – YouTube. You also find this talk on Sutter’s blog, Sutter’s Mill – Herb Sutter on software development.
Context
Say you have a switch-case bloc with no fallthrough (this is important), like this one:
enum class Foo {
Alpha,
Beta,
Gamma,
};
int main()
{
std::string s;
Foo f;
// ...
// Do things with s and f
// ...
switch (f)
{
case Foo::Alpha:
s += "is nothing";
break;
case Foo::Beta:
s += "is important";
f = Foo::Gamma;
break;
case Foo::Gamma:
s += "is very important";
f = Foo::Alpha;
}
// ...
}
Nothing fantastic to say about this code: it appends a suffix to the string depending on the value of f
, sometimes changing f
at the same time.
Now, let’s say we add a Delta
to the enum class Foo
, which does exactly like Gamma
, but with a small difference in the text. This has a good chance to be the result:
enum class Foo {
Alpha,
Beta,
Gamma,
Delta,
};
int main()
{
std::string s;
Foo f;
// ...
// Do things with s and f
// ...
switch (f)
{
case Foo::Alpha:
s += "is nothing";
break;
case Foo::Beta:
s += "is important";
f = Foo::Alpha;
break;
case Foo::Gamma:
s += "is very important";
f = Foo::Alpha;
case Foo::Delta:
s += "is not very important";
f = Foo::Alpha;
}
// ...
}
The new case block is obviously copy-pasted. But did you notice the bug?
Since in the first version, the developer of this code did not feel it necessary to put break
at the end, when we copy-pasted the Gamma
case we left it without break
. So there will be an unwanted fallthrough in this switch.
New syntax
The new syntax presented in this article makes this kind of mistake less likely and makes the code a bit clearer.
Here it is:
switch (f)
{
break; case Foo::Alpha:
s += "is nothing";
break; case Foo::Beta:
s += "is important";
f = Foo::Alpha;
break; case Foo::Gamma:
s += "is very important";
f = Foo::Alpha;
break; case Foo::Delta:
s += "is not very important";
f = Foo::Alpha;
}
This is it: we put the break
statement before the case
.
This may look strange to you since the very first break
is useless and there is no closing break
in the last case block, but this is really functional and convenient.
If you begin each of your case block with break; case XXX:
, you will never have a fallthrough bug ever again.
Benefits
The first benefit is the avoidance of the presented bug in the first section, when you forget to add a break
when adding a case
block. Even if you don’t copy-paste to create your new block, it’ll be visually obvious if you forget the break
(your case
statement won’t be aligned with the others).
But the real benefit (in my opinion) is that the syntax is, overhaul, nicer. For each case, you save a line by not putting the break
within the case
block, and everyone will notice at first sight that the switch-case has no fallthrough.
Of course, beauty is subjective. That includes the beauty of code. However, things like better alignment, clearer intentions, and line economy1 are, it seems to me, quite objective as benefits.
Disclaimer
The first time I saw this syntax, I quickly understood how it worked and why it was better than the “classic” syntax. However, I know that several people were confused and had to ask for an explanation.
But that’s almost always the case when introducing a new syntax.
So keep in mind that your team may be confused at first if you use it in a shared codebase. Be sure to explain (either in person or in the comments) so people can quickly adapt to this new form.
Wrapping up
This is certainly not a life-changing tip that is presented here, but I wanted to share it because I really like how it looks.
It’s yet another brick in the wall of making your code prettier.
Thanks for reading and see you next week.
Author: Chloé Lourseyre
Editor: Peter Fordham
Addendum
Notes
- “Line economy” is beneficial when it discards non-informative statements, just like the
break
is in this context. I would never say huge one-liners are better than a more detailed block of code (because they aren’t). Reuniting thebreak
and thecase
keywords let your code breathe (you can put an empty line in place of thebreak
if you want to keep space).
Interesting. It would be nice to know if any code formatters like clang-format support this, and how. Nowadays companies will often trade « optimum » formatting for something that is good enough, uniform and prevents case-by-case discussions.
This is nice, but it is not a new syntax, it is a new code formatting convention. A little macro can convert it to a real new syntax: https://compiler-explorer.com/z/Gh5zGqPjK
Code checking tools like JSHint will warn you about the first thing in the `switch` block not being a `case` label. However, if you’re using such a thing, it will probably also warn you if you accidentally omit the `break` statement before the second `case` label et seq.