views:

225

answers:

6

During debug mode or while I am doing testing, I need to print lots of various information, so i use this method:

#ifdef TESTING
// code with lots of debugging info
#else
// clean code only
#endif // TESTING`

Is this a good method, or is there any other simple and elegant method ?

But this way, I am repeating the same code in two places and if anything is to be changed later on in the code, I have to do it in both places, which is time consuming and error prone.

Thanks.

I am using MS Visual Studio.

+12  A: 

You could use a macro to print debug information and then in the release build, define that macro as empty.

e.g.,

#ifdef _DEBUG
#define DEBUG_PRINT(x) printf(x);
#else
#define DEBUG_PRINT(x)
#endif

Using this method, you can also add more information like

__LINE__ 
__FILE__

to the debug information automatically.

Tuomas Pelkonen
Macros are bad c++ style.
Alexey Malistov
Maybe, but the question was tagged both C and C++.
Tuomas Pelkonen
I am not agree with Alexey, I write years with c++ and Macros with out any problem. Maybe is bad c++ style, for bad programmers that don't know what they do, but only with macros I can do some very difficult thinks. The freedom to use many ways is not that bad :)
Aristos
Macros tend to make it easier to write type-unsafe, complicated, and/or unclear code. Some things you can't do in any other way, and macros are better than re-writing the same code by copy-paste.
Mark B
Is there any alternative to using Macros, as in the case above when it is the most simple solution ?
john
You are stuck with macros to invoke `__LINE__` and `__FILE__` (and if you wish `BOOST_CURRENT_FUNCTION` though it's longish. That's one of those rare exceptions...
Matthieu M.
Since the macro discussion has been done, there's two other problems. First, there should be no semicolons in either version. As given, DEBUG_PRINT(x) will either provide a statement or not, and that can be semantically important. Second, `puts(x)` is better than `printf(x)`, since in the only cases where they differ (embedded '%') `printf(x)` will screw things up.
David Thornley
+2  A: 

Use a define like that on include headers

#ifdef  TESTING
#define DEBUG_INFOS(_X_) CallYourDebugFunction(_X_ )
#else
#define DEBUG_INFOS(_X_) ((void)0)
#endif

and then use only this on your code

...
DEBUG_INFOS("infos what ever");
RestOfWork();
...

You can also use and search for the ASSERT and TRACE macros and use the DebugView from sysinternals to read the output real time from trace, or track problems with the ASSERT. ASSERT and TRACE do similar work, and you can get ideas from them.

comments: I use the TESTING declare, because I see that on the question.

Aristos
+4  A: 

Write once

#ifdef _DEBUG 
const bool is_debig = true;
#else 
const bool is_debig = false;
#endif 

and then

template<bool debug>
struct TemplateDebugHelper {
   void PrintDebugInfo(const char* );
   void CalcTime(...);
   void OutputInfoToFile(...);
   /// .....
};

// Empty inline specialization
template<>
struct TemplateDebugHelper<false> {
   void PrintDebugInfo(const char* ) {} // Empty body
   void CalcTime(...) {} // Empty body
   void OutputInfoToFile(...) {} // Empty body
   /// .....
};

typedef TemplateDebugHelper<is_debug> DebugHelper;

DebugHelper global_debug_helper;

int main() 
{
   global_debug_helper.PrintDebugInfo("Info"); // Works only for is_debug=true
}
Alexey Malistov
Can I ask you if this left any code on compiled files, on release mode ?
Aristos
At worst, it will be an empty function cal. However, since the code has to be available to the compiler, in my experience even that gets optimized away.
Dennis Zickefoose
I like this answer except that my experience is that if a programmer has to write 'global_debug_helper.PrintDebugInfo' he's not going to bother. I generally use short all caps so they're easy to write, but easy to spot:BUG.OUT("Info");
swestrup
@Dennis: it's not the function call I worry about, but the evaluation of the arguments. I am not sure that the template solution with allow the compiler to skip the evaluation of arguments, and when you're dumping heavily in debug mode, you WANT it to be removed.
Matthieu M.
That would depend entirely on what you're doing in the arguments. If the compiler can tell that there are no side effects, then it will happily skip them.
Dennis Zickefoose
+1  A: 

You could use something like boost::log setting severity level to the one you need.

void init()
{
    logging::core::get()->set_filter
    (
        flt::attr< logging::trivial::severity_level >("Severity") >= logging::trivial::info
    );
}

int main(int, char*[])
{
    init();

    BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
    BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
    BOOST_LOG_TRIVIAL(info) << "An informational severity message";
    BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
    BOOST_LOG_TRIVIAL(error) << "An error severity message";
    BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
}

I think pantheios have something similar too.

Dmitry Yudakov
+2  A: 

Use Log4Cxx, instead of rolling your own logging. The Log4Cxx package is highly configurable, supports different levels of logging based on importance/severity, and supports multiple forms of output.

In addition, unless it is very critical code that must be super optimized, I would recommend leaving logging statements (assuming you use Log4Cxx) in your code, but simply turn down the logging level. That way, logging can be dynamically enabled, which can be incredibly helpful if one of your users experiences a hard-to-replicate bug... just direct them on how to configure a higher logging level. If you completely elide the logging from the executable, then there is no way to obtain that valuable debugging output in the field.

Michael Aaron Safyan
This.In my team's project, we found compiling in the TRACE messages didn't have a noticable impact on performance unless we actually enable tracing, and the beauty of the log4xxx frameworks is you can do that for some log sources but not others, depending upon what you're trying to target.There are a few places in our code where the arguments to the trace log call are expensive to compute; for these, we just call Logger::isEnabledFor(LogLevelTrace) to detect when tracing is on for a given logger; if so we build the expensive args and log the trace message, else we leave it alone.
anelson
@anelson, why didn't you use LOG4CXX_TRACE(LOGGER,MSG)? I believe that macro already does that check for you.
Michael Aaron Safyan
A: 

I'm writing for embedded systems in C. In my programs I'm using following macros:

#define _L  log_stamp(__FILE__, __LINE__)

#define _LS(a)  log_string(a)

#define _LI(a)  log_long(a)

#define _LA(a,l)  log_array(a,l)

#define _LH(a)  log_hex(a)

#define _LC(a)  log_char(a)

#define ASSERT(con) log_assert(__FILE__, __LINE__, con)

When I'm making release version, I simply switch off #define DEBUG directive and all macros become empty. Note that it doesn't consume any CPU cycles and memory in release version. Macros are the only way to save to log information: where the logging was done (file and line number).

If I need this information I use: _L;_LS("this is a log message number ");_LI(5);

otherwise without _L directive.

mack369