Comments and Debug Code
February 15, 2021
Blog
Developing software for embedded systems takes time – generally more time than initially expected.
One reason for this is that, whilst initial coding may progress swiftly, cycles of debugging can seem interminable. Later on, maintenance of code is inevitable and also takes more time than expected. This is because there is an overhead associated with understanding exactly how the code functions before modifications are attempted.
The upshot of this observation is that a very high priority should be given to the readability of code. Easily readable code is less likely to have bugs, so debugging time is reduced. If code is readily understood, it is more amenable to maintenance. So, always keep the human reader in mind – after all, it might be you. (I have heard it suggested that you should assume that a future maintainer of your code is a psychopath who knows your home address.)
Code consists of two things: stuff the compiler needs to see (C statements and declarations) and text that needs to be hidden from the compiler (which is mostly there for the human reader). The hidden stuff is primarily:
- Documentation – comments in the code
- Code than has been temporarily removed – an inevitable part of the debugging process
- Debug/trace code – which can be switched on and off, as required
Interestingly, if you take out the stuff that is purely for the human reader (and that also includes the use of meaningful identifiers and extra whitespace, like newlines), the result is code that is totally unreadable. This has been exploited by some software companies to distribute source code, while protecting their intellectual property.
I will take a look at the things that we hide from the compiler.
Documentation
Everyone knows that comments are a good idea, but we are all lazy. However, the effort is well worthwhile. Old style /* ... */ C comments are OK, but I feel the C++ end-of-line [ //... ] variety tend be clearer. They still need to be used with care. For example, code like this:
is so hard to follow. Alignment is everything:
And do not use tabs. They are not portable.
Temporary Code Removal
In the process of debugging and testing code, it is commonly useful to be able to “switch off” a chunk of code temporarily. Many programmers “comment out” code to achieve this result by putting /* at the beginning and */ at the end. That is quick and dirty, but frequently fails to achieve the required result. Many compilers do not support nested comments, so, if the code was already commented, problems arise. Overall, it is error prone and should be avoided.
The use of C++ style // comment notation – i.e., putting // at the beginning of each line – is marginally better, but very tedious to apply and may also be error prone in removal.
The best way to achieve this result is by use of pre-processor directives, thus:
In any case, “turned off” code sequences should not be included in any code which is being considered for release.
Debug/Trace Code
Another kind of temporary code is that included to facilitate the output or logging of extra information when debugging. Although modern debuggers and such tools can be very effective, sometimes instrumenting the code can still be the best way to figure out exactly what is going on. Modern development tools are so fast that rebuilding to create a debug-enabled image is not a serious overhead.
A common way to facilitate this is using pre-processor directives, thus:
So, when the symbol DEBUG_TRACE is defined, the debug code is included.
A slightly different approach is to code it like this:
This double negative seems clumsy, but this symbol is used to control the standard assert() macro. The programmer needs to define the symbol to suppress debugging mode. I acknowledge Michael Barr, who raised my awareness of this approach in Embedded C Coding Standard.
#endif
In any case, “turned off” code sequences should not be included in any code which is being considered for release.
Debug/Trace Code
Another kind of temporary code is that included to facilitate the output or logging of extra information when debugging. Although modern debuggers and such tools can be very effective, sometimes instrumenting the code can still be the best way to figure out exactly what is going on. Modern development tools are so fast that rebuilding to create a debug-enabled image is not a serious overhead.
A common way to facilitate this is using pre-processor directives, thus:
#ifdef DEBUG_TRACE
#endif
So, when the symbol DEBUG_TRACE is defined, the debug code is included.
A slightly different approach is to code it like this:
#ifndef NDEBUG
#endif
This double negative seems clumsy, but this symbol is used to control the standard assert() macro. The programmer needs to define the symbol to suppress debugging mode. I acknowledge Michael Barr, who raised my awareness of this approach in Embedded C Coding Standard.
#endif
In any case, “turned off” code sequences should not be included in any code which is being considered for release.
Debug/Trace Code
Debug/Trace Code
Another kind of temporary code is that included to facilitate the output or logging of extra information when debugging. Although modern debuggers and such tools can be very effective, sometimes instrumenting the code can still be the best way to figure out exactly what is going on. Modern development tools are so fast that rebuilding to create a debug-enabled image is not a serious overhead.
A common way to facilitate this is using pre-processor directives, thus:
#ifdef DEBUG_TRACE
#endif
So, when the symbol DEBUG_TRACE is defined, the debug code is included.
A slightly different approach is to code it like this:
#ifndef NDEBUG
#endif
This double negative seems clumsy, but this symbol is used to control the standard assert() macro. The programmer needs to define the symbol to suppress debugging mode. I acknowledge Michael Barr, who raised my awareness of this approach in Embedded C Coding Standard.
#endif
In any case, “turned off” code sequences should not be included in any code which is being considered for release.
Debug/Trace Code
Debug/Trace Code
Another kind of temporary code is that included to facilitate the output or logging of extra information when debugging. Although modern debuggers and such tools can be very effective, sometimes instrumenting the code can still be the best way to figure out exactly what is going on. Modern development tools are so fast that rebuilding to create a debug-enabled image is not a serious overhead.
A common way to facilitate this is using pre-processor directives, thus:
#ifdef DEBUG_TRACE
#endif
So, when the symbol DEBUG_TRACE is defined, the debug code is included.
A slightly different approach is to code it like this:
#ifndef NDEBUG
#endif
This double negative seems clumsy, but this symbol is used to control the standard assert() macro. The programmer needs to define the symbol to suppress debugging mode. I acknowledge Michael Barr, who raised my awareness of this approach in Embedded C Coding Standard.