I often wrap things like debug sonar in a simple macro that allows it to be compiled out of release builds:
#ifdef DEBUG
#define D(s) do { s; } while(0)
#else
#define D(s) do {/**/} while(0)
#endif
Usage later is typically something like:
D(printf("level %d, condition %s\n", level, condition));
The do{}while(0)
idiom is there to avoid issues that might result from accidentally making a usage of D(...)
the only content of a conditional or loop. You don't want code like this to mean the wrong thing, after all:
for(i=1;i<10;++i) D(printf("x[%d]=%f\n",i,x[i]));
SomeReallyExpensiveFunction(x);
If I could make that case throw an error, I would, but the preprocessor would have to be a full compiler itself to tell that the D()
macro was the sole content of a loop body.
I'm also a big fan of compile-time assertions. My formulation is slightly different, but has no real advantages over others I've seen. The key is to form a uniquely named typedef that throws an error if the asserted condition is false, and not otherwise. In cassert.h we have:
/*! \brief Compile-time assertion.
*
* Note that the cassert() macro generates no code, and hence need not
* be restricted to debug builds. It does have the side-effect of
* declaring a type name with typedef. For this reason, a unique
* number or string of legal identifier characters must be included
* with each invocation to avoid the attempt to redeclare a type.
*
* A failed assertion will attempt to define a type that is an array
* of -1 integers, which will throw an error in any standards
* compliant compiler. The exact error is implementation defined, but
* since the defined type name includes the string "ASSERTION" it
* should trigger curiosity enough to lead the user to the assertion
* itself.
*
* Because a typedef is used, cassert() may be used inside a function,
* class or struct definition as well as at file scope.
*/
#define cassert(x,i) typedef int ASSERTION_##i[(x)?1:-1]
And in some source file, anywhere a typedef would be legal:
#include "cassert.h"
...
cassert(sizeof(struct foo)==14, foo1);
...
The resulting error message is often obscure, but will contain the fragment of identifier enabling the offending line to be discovered by brute force.
I've been guilty of using the preprocessor in places where writing a code generation utility might have been the preferred answer, much like the code in another answer that generated lots of boiler-plate based on the unique parts of an enum member's name. That is especially handy when writing a lot of message-dispatch glue to be compiled in C.