Reputation: 2890
Is there a "best practice" or similar for coding in a debug-mode in one's code? For example,
#include <iostream>
int main()
{
#ifdef MY_DEBUG_DEF
std::cout << "This is only printed if MY_DEBUG_DEF is defined\n";
#endif
return 0;
}
Or is this considered bad practice because the code gets bit messier? I have noticed some libraries (for example libcurl, which is a large and well-known library) have this feature; if you define VERBOSE with libcurl you get basically a debug mode
Thank you.
Upvotes: 3
Views: 2525
Reputation: 129374
I personally prefer runtime enabled logging. That means that you don't have to recompile to get the "debug output". So I have a command-line argument -v=n
, where n
defaults to zero, and gets stored in the variable verbosity
in the actual program. With -v=1
I get basic tracing (basic flow of the code), with -v=2
I get "more stuff" (such as dumps of internal state in selected functions). Two levels is often enough, but three levels may be good at times. An alternative is to make verbosity
a bit-pattern, and enable/disable certain functionality based on which bits are set - so set bit 0
for basic trace, bit 1
gives extra info in some module, bit 2 gives extra trace in another module, etc. If you want to be REALLY fancy, you have names, such as -trace=all_basic
, -trace=detailed
, -trace=module_A
, -trace=all_basic,module_A,module_B
or some such.
Combine this with a macro along the lines of:
#define TRACE do { if (verbosity > 0) \
std::cout << __FILE__ << ":" << __LINE__ << ":" \
<< __PRETTY_FUNCTION__ << std::endl; } while(0)
For things that may take a substantial amount of extra time, such as verifying the correctness of a large and complex data structure (tree, linked list, etc), then using #ifndef NDEBUG
around that code would be a good thing. Assuming of course you believe that you'll never mess that up in a release build.
Real livig code here: https://github.com/Leporacanthicus/lacsap/blob/master/trace.h https://github.com/Leporacanthicus/lacsap/blob/master/trace.cpp being use here for example: https://github.com/Leporacanthicus/lacsap/blob/master/expr.cpp
(Note that some simple functions that get called a lot don't have "TRACE" - it just clutters up the trace and makes it far too long)
Upvotes: 1
Reputation: 1
A more usual way is to follow conventions from assert(3): wrap with #ifndef NDEBUG
.... #endif
code which is only useful for debugging, and without any significant side effects.
You could even add some debug-printing macro like
extern bool wantdebug;
#ifndef NDEBUG
#define OUTDEBUG(Out) do { if (wantdebug) \
std::cerr << __FILE__ << ":" << __LINE__ \
<< " " << Out << std::endl; \
} while(0)
#else
#define OUTDEBUG(Out) do {}while(0)
#endif
and use something like OUTDEBUG("x=" << x)
at appropriate places in your code. Then wantdebug
flag would be set thru the debugger, or thru some program arguments. You probably want to emit a newline and flush cerr
(or cout
, or your own debug output stream) -using std::endl
...- to get the debug output displayed immediately (so a future crash of your program would still give sensible debug outputs).
Upvotes: 4
Reputation: 1536
Using logger may be better, e.g.
The above are very flexible, but heavyweight. You can also implement some simple macros instead:
#ifdef NDEBUG
#define DBG(FMT, ...)
#else // !NDEBUG
#define DBG(FMT, ...) fprintf (stderr, FMT, ## __VA_ARGS__)
#endif // NDEBUG
The above is GCC syntax from Macros with a Variable Number of Arguments. For VC, please see also How to make a variadic macro (variable number of arguments)
Upvotes: 0
Reputation: 4873
That is an acceptable method. Yes, it gets messy, but it makes it a lot easier to debug as well. Plus you can normally collapse those kinds of things, thus removing the messiness.
As you've mentioned, libcurl uses the method. I also used to have a teacher who works for HP on their printer software, and they used the same method.
Upvotes: 1