So part two of this series actually focuses on the use of macros, arguing about the perceived good and bad style and why it matters, before we get back on track with linked list performance… :)
Macros have long been the bane of C programming: they are stiff, hard to maintain, hard to read and so on. Well, I found that most issues are not directly related to macros. It seems that the matter is rather trivial: macros are a different world for most C programmers and many have never worked with them. Instead, let’s focus on a couple of benefits that macros can offer.
Personally, I find this one the most important aspect of macros. Refactoring code that is essentially the same when it’s not. Function names can be build and called using macros, input arguments can have different names. It’s even possible to generate a bunch of named functions using a macro constructor.
Convoluted code can be broken down into several macros to better reflect the logic steps involved. These macros can be documented away from the code itself. It’s a thin red line, but the improved readability and effort to break down logic directly translate into code quality.
Injecting a simple operation into the code instead of a function (or worse, using function pointer) can make all the difference in a hot execution path. Being able to let the compiler take care of these things is a good way to focus on algorithmic bottlenecks instead of trying to outsmart the compiler by injecting runtime optimisations.
Here Be Dragons
On the flip-side there are things to avoid like side effects, meaning try to put all arguments used into the macro declaration. Manipulating variables outside the macro’s immediate scope is feasible, but also counter-intuitive, e.g. running a macro DO_STUFF. Contrariwise, output = CALCULATE_SUM(input) at least gives the reader a hint of what is being included in the operation. And, unusally, it’s good practice to include temporary variables so that variable shadowing or general name clashing doesn’t occur (these are very annoying bugs; the compiler will tell you a lot of garbage)… output = CALCULATE_SUM(temp_var, input).
The bounding of macro arguments is also important. The general rule is always use parentheses around arguments in the macro body to not break arithmetics or order of execution. The exception is array indexing with single arguments, e.g. buf[x], and single function arguments separated using commas as they are bounded directly. However, care must be taken with bounding macro arguments that are macros themselves: bounding them will enforce strict matching to function pointers only.
I’m sure there’s more to talk about, but for now let’s focus on this: what can we do? We can educate and advocate the sane use of macros inside C code. We can give programmers a day or two to get to know the depths of macros. There is a lot there that directly translates to skill in C and also programming in general. It broadens the mind, and should you find out macros are not for you that’s okay, but don’t take them away from the rest of the world, as they are, like everything a way to enrich the work and art we deal with every day.