I’ve recently begun working on an embedded C project where the code has got pretty out of hand and I’m tasked with bringing some sanity to the code.
It quickly reaffirms my main focus of good software development. Nice simple composable functions. Everything about good design boils down to writing good functions.
Refactoring bad C to good C is a bit of a mission. But my first whack at doing it is always “Extract Method” (well, other than removing the million warnings that the C compiler helpfully told you about but let you carry on anyways…..0 warnings should be a C coders mantra) .
When I extract methods Its usually at the BIG and tiny level of the code….. either I’m taking a huge bunch of code and wrapping it in a function, or taking a really tiny piece and wrapping it (like if a common piece of checking code is scattered all over the place
eg I’d take :- if(strnicmp(blah,”MAGICPREFIX:”,12))
and make it :- if(has_magic_prefix(blah))
it slowly declutters the code….and in the end the “has_magic_prefix” may even get totally refactored out of the system. But for starters, micro decluttering makes a world of difference.
From there I start to focus on what the code is really really really doing. Then start designing a system of functions that allow you to easily express that idea.
Also its important to group C functions into .c files ( treat .c files as a “class”)
That group of C functions should work on a set of common structures ( just like classes ).
Simply by wrapping common packets of data often gets rid of the “millions of parameters” functions.
All of a sudden things get a LOT simpler, A LOT easier to extend and a lot more composable!
Add in a nice system of callbacks to be able to inject variations for special cases rather than having masses of if/elses or switch statements, you are well on your way to writing some pretty nice C code.