I just encountered a DEBUG macro in C that I really like
#ifdef DEBUG_BUILD
# define DEBUG(x) fprintf(stderr, x)
#else
# define DEBUG(x) do {} while (0)
#endif
I'm guessing a C++ analogue would be :-
#ifdef DEBUG_BUILD
# define DEBUG(x) cerr << x
#else
# define DEBUG(x) do {} while (0)
#endif
- Is the second code snippet analogous to the one in C?
- Do you have any favorite C++ debug macros?
EDIT : By "Debug Macros" I mean "macros that might come in handy while running a program in debug mode".
Is the second code snippet analogous to the one in C?
More or less. It's is more powerful, as you can include
<<
-separated values in the argument, so with a single argument you get something that would require a variable number of macro arguments in C. On the other hand, there is a slim chance that people will abuse it by including a semicolon in the argument. Or even encouter mistakes due to a forgotten semicolon after the call. So I'd include this in a do block:Do you have any favorite C++ debug macros?
I like the one above, and use it quite often. My no-op usually just reads
which has the same effect for optimizing compilers. Although the comment by @Tony D below is corret: this can leave some syntax errors undetected.
I sometimes include a run-time check as well, thus providing some form of a debug flag. As @Tony D reminded me, having an endl in there is often useful as well.
Sometimes I also want to print the expression:
In some macros, I like to include
__FILE__
,__LINE__
or__func__
, but these are more often assertions and not simple debug macros.For question 1] Answer is yes. It will just print the message to standard error stream.
For question 2] There are many. My Fav is
#define LOG_ERR(...) fprintf(stderr, __VA_ARGS__)
which will allow one to include arbitrary number of variables to include in the debug message.
This is my version, using a variadic template
print
function:The version I makes the
debug_print
a variadic template function which accepts a debug level which allows me to select what kind of output I want to output at runtime:Note the
print
function crashes Visual Studio 2013 Preview (I haven't tested the RC). I have noticed it is faster (on Windows, where console output is slow) than my previous solution which used anostream
child class that overloadedoperator<<
.You can also use a temporary
stringstream
insideprint
if you only want to call the real output function once (or write your own typesafeprintf
;-))… and as addendum to all responses:
Personally I never use macros like
DEBUG
to distinct debug from release code, instead I useNDEBUG
which is must be defined for release builds to eliminateassert()
calls (yes, I useassert()
extensively). And if latter is not defined, then it is a debug build. Easy! So, actually there is no reason to introduce one more debug macro! (and handle possible cases whenDEBUG
andNDEBUG
both are not defined).I use following micro,
USE:
Here's my favorite
It's super handy and makes for clean (and importantly, fast in release mode!!) code.
Lots of
#ifdef DEBUG_BUILD
blocks all over the place (to filter out debug related blocks of code) is pretty ugly, but not so bad when you wrap a few lines with aD()
.How to use:
If that's still too ugly/weird/long for you,
(I suggest not using
using namespace std;
though maybeusing std::cout; using std::cerr;
could be a good idea)Note that you might want to do more things than just print to stderr when you are thinking about "debugging". Get creative, and you can build constructs that offer insight into the most complex interactions within your program, while allowing you to very quickly switch to building a super efficient version unencumbered by debug instrumentation.
For example in one of my recent projects I had a huge debug-only block which started with
FILE* file = fopen("debug_graph.dot");
and proceeded to dump out a graphviz compatible graph in dot format to visualize large trees within my datastructures. What's even cooler is the OS X graphviz client will auto-read the file from disk when it changes, so the graph refreshes whenever the program is run!I also particularly like to "extend" classes/structs with debug-only members and functions. This opens up the possibility of implementing functionality and state that is there to help you track down bugs, and just like everything else that is wrapped in debug macros, is removed by switching a build parameter. A giant routine that painstakingly checks each corner case on every state update? Not a problem. Slap a
D()
around it. Once you see it works, remove-DDEBUG
from the build script, i.e. build for release, and it's gone, ready to be re-enabled at a moment's notice for your unit-testing or what have you.A large, somewhat complete example, to illustrate (a perhaps somewhat overzealous) use of this concept:
Notice that for large blocks of code, I just use regular block
#ifdef
conditionals because that improves readability somewhat, as for large blocks the use of extremely short macros is more of a hindrance!The reason why the
N(x)
macro must exist is to specify what to add when unit-testing is disabled.In this part:
It would be nice if we could say something like
But we cannot, because the comma is a part of preprocessor syntax. Omitting the comma produces invalid C++ syntax.
If you had some additional code for when not compiling for debug, you could use this type of corresponding inverse-debug macro.
Now this code might not be an example of "really good code" but it illustrates some of the things that you can accomplish with clever application of macros, which if you remain disciplined about, are not necessarily evil.
I came across this gem just now after wondering about the
do{} while(0)
stuff, and you really do want all that fanciness in these macros as well!Hopefully my example can provide some insight into at least some of the clever things that can be done to improve your C++ code. It is really valuable to instrument code while you write it rather than to come back to do it when you don't understand what's happening. But it is always a balance that you must strike between making it robust and getting it done on time.
I like to think of additional debug build sanity checks as a different tool in the toolbox, similar to unit tests. In my opinion, they could be even more powerful, because rather than putting your sanity check logic in unit tests and isolating them from the implementation, if they are included in the implementation and can be conjured at will, then complete tests are not as necessary because you can simply enable the checks and run things as usual, in a pinch.