At my day job (I just made a new category for these posts) we have been working on an official coding standard to use for our software projects. When I was hired five years ago, I was given a three-page document about “Software Best Practices” that served as a casual guide to how code should be formatted and how functions and variables should be named. From looking at the million+ lines of code I maintain, it is clear that some items were adhered to, while others were ignored completely (thankfully; I disliked the suggested variable naming).
BARR-C focuses on embedded C programming and, unlike the coding standards I have seen at other jobs, it focuses on bug reduction rather than making the code pretty. I purchased a physical copy of the book, but you can download a PDF of it for free:
https://barrgroup.com/sites/default/files/barr_c_coding_standard_2018.pdf
While I consider myself quite stubborn or stuck in my ways, I can flip on a dime if presented compelling new information. The BARR-C standard is making me rethink a few things. Here is one example, which I share with you to get your take on it.
switch and case
// The way I have been using
switch (color)
{
case RED:
stop();
break;
case YELLOW:
slow();
break;
case GREEN:
speed();
break;
default:
break;
}
I am very used to seeing the statements indented past the “case”. But, one of the editors I work with constantly moves those statements back to where they line up with the switch:
// The way one of my editors wants me to type it:
switch (color)
{
case RED:
stop();
break;
default:
break;
}
I certainly don’t like the idea of code being at the same level of the braces. This seems like an odd default to me. Have you seen this elsewhere? ‘prolly has some specific name for this convention.
And recently, I ran into something new that has been added by a later C standard than any I have used. It does not allow variables to be declared inside the switch! That seemed odd, since–at some point–the C standard moved away from “all variables must be declared at the top of the function” to “yeah, wherever you want, it’s fine.”
void function ()
{
int counter; // We used to have to do this only here.
...some lines later...
while (active)
{
int counter = 0; // But now we can do it here.
// ...stuff...
}
}
Today, I prefer “variables used just in this bit of code” to be declared around that code so it is much easier to see what that variable is used for. Of course, this wouldn’t matter if every function was small enough to completely fit on the screen at the time. Sadly, I always seem to work with legacy functions that are hundreds of lines long.
But I digress…
There is something new (to me) that now disallows declaring variables in the case. I have seen this done (and still do it myself) for many years. To make it work, you need an extra set of curly braces which causes switch/cases that look like this:
switch (color)
{
case RED:
{
int x; // Now works because braces.
// ...stuff...
break;
}
default:
break;
}
I suppose this has advantages. It is making a scoped (is that the term?) variable just inside those braces of the case. Each case could make its own “x” and use it, if it wanted to, I suppose.
Side Note: Of course I had to try this out. Indeed, by default, this works without a peep from the compiler, but if you enable the proper warnings you will at least get “warning: declaration of ‘x’ shadows a previous local [-Wshadow]”.
int main()
{
int x = 42;
switch (x)
{
case 1:
{
int x = 1;
printf ("x = %d\n", x);
break;
}
case 2:
{
int x = 2;
printf ("x = %d\n", x);
break;
}
default:
printf ("x = %d\n", x);
break;
}
return 0;
}
But that’s not important to this story… The BARR-C is giving me a new formatting option, and a reason why I might want to do it. It lines up the “case” and “break” together:
switch (err)
{
case ERR_A:
...
break;
case ERR_B:
...
// Also perform the steps for ERR_C.
case ERR_C:
...
break;
default:
...
break;
}
I have never encountered the case/default and its break lined up like that before. It looks odd to me, and feels wrong. But the reason for this is given:
Reasoning: C’s switch statements are powerful constructs, but prone to errors such as omitted break statements and unhandled cases. By aligning the case labels with their break statements it is easier to spot a missing break.
– Embedded C Coding Standard, Michael Barr
Interesting. I have, on a number of occasions (including again recently), found a bug where a break was missing, or something happened where it got backspaced to the line above it where it was now at the end of a comment:
case GIVE_UP:
// Give up and return.break;
default:
// Never surrender!!!
break;
This is a trivial example, but if there had actually been lines of code there, you’d have to look at the last line of each case to verify a break was there. But if you line up the case/break like this…
case GIVE_UP:
// Give up and return.break;
default:
// Never surrender!!!
break;
…you can immediately notice a problem where the “patterns don’t match,” which our brains seem to notice easier.
Using curly braces would not make a missing break stand out — in fact, it might make you assume it is all good because you see the closing brace there.
So I kinda like it.
Even if I hate it.
What say you? Comments if you got ’em.
Side Note 2: Since I originally typed this in, I have now fully converted to this formatting look. And, it has already helped me spot an issue like the one I mentioned earlier — without me having to scrounge line by line through the code trying to figure out what is going on. Nice.
Until next time…
