DEBUG macros in C++

后端 未结 9 1894
谎友^
谎友^ 2020-12-07 07:54

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)
#e         


        
相关标签:
9条回答
  • 2020-12-07 08:22

    I like to use macros with __LINE__, __FILE__ as arguments to show where in the code the printout is from - it's not uncommon to print the same variable name in several places, so fprintf(stderr, "x=%d", x); won't mean much if you then add another one the same ten lines further down.

    I've also used macros that override certain functions and store where it was called from (e.g. memory allocations), so that later on, I can figure out which one it was that leaked. For memory allocation, that's a little harder in C++, since you tend to use new/delete, and they can't easily be replaced, but other resources such as lock/unlock operations can be very useful to trace this way [of course, if you have a locking wrapper that uses construction/destruction like a good C++ programmer, you'd add it to the constructor to add file/line to the internal structure once you have acquired the lock, and you can see where it's held elsewhere when the you can't acquire it somewhere].

    0 讨论(0)
  • 2020-12-07 08:22

    I use the code below for logging. There are a few advantages:

    1. I can turn them on/off at runtime.
    2. I can compile out statements at a particular log level. For example, at the moment, I've unconditionally compiled in the KIMI_PRIVATE macro because I'm debugging something in the release build but since there is a lot of potentially secret sauce stuff being logged (lol), I compile it out of release builds.

    This pattern has served me very well over the years. Note: although there is a global logMessage function, the code usually queues the log to a logging thread.

    #define KIMI_LOG_INTERNAL(level,EXPR)           \
      if(kimi::Logger::loggingEnabled(level))       \
      {                                             \
        std::ostringstream os;                      \
        os << EXPR;                                 \
        kimi::Logger::logMessage(level ,os.str());  \
      }                                             \
      else (void) 0
    
    #define KIMI_LOG(THELEVEL,EXPR)                 \
      KIMI_LOG_INTERNAL(kimi::Logger::LEVEL_ ## THELEVEL,EXPR)
    
    #define KIMI_ERROR(EXPR)   KIMI_LOG(ERROR,EXPR)
    #define KIMI_VERBOSE(EXPR) KIMI_LOG(VERBOSE,EXPR)
    #define KIMI_TRACE(EXPR)   KIMI_LOG(TRACE,EXPR)
    #define KIMI_INFO(EXPR)    KIMI_LOG(INFO,EXPR)
    #define KIMI_PROFILE(EXPR) KIMI_LOG(TRACE,EXPR)
    
    // Use KIMI_PRIVATE for sensitive tracing
    //#if defined(_DEBUG)
    #  define KIMI_PRIVATE(EXPR) KIMI_LOG(PRIVATE,EXPR)
    // #else
    // #  define KIMI_PRIVATE(EXPR) (void)0
    // #endif
    
    0 讨论(0)
  • 2020-12-07 08:24

    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.

    0 讨论(0)
  • 2020-12-07 08:26

    This is my version, using a variadic template print function:

    template<typename... ArgTypes>
    inline void print(ArgTypes... args)
    {
      // trick to expand variadic argument pack without recursion
      using expand_variadic_pack = int[];
      // first zero is to prevent empty braced-init-list
      // void() is to prevent overloaded operator, messing things up
      // trick is to use the side effect of list-initializer to call a function
      // on every argument.
      // (void) is to suppress "statement has no effect" warnings
      (void)expand_variadic_pack{0, ((cout << args), void(), 0)... };
    }
    
    #ifndef MYDEBUG
    #define debug_print(...)
    #else
    #define debug_print(...) print(__VA_ARGS__)
    #endif
    

    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:

    template<typename... ArgTypes>
    inline void debug_print(debug::debug level, ArgTypes... args)
    {
      if(0 != (debug::level & level))
        print(args...);
    }
    

    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 an ostream child class that overloaded operator<<.

    You can also use a temporary stringstream inside print if you only want to call the real output function once (or write your own typesafe printf ;-))

    0 讨论(0)
  • 2020-12-07 08:29

    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 encounter mistakes due to a forgotten semicolon after the call. So I'd include this in a do block:

    #define DEBUG(x) do { std::cerr << x; } while (0)
    

    Do you have any favourite C++ debug macros?

    I like the one above and use it quite often. My no-op usually just reads

    #define DEBUG(x)
    

    which has the same effect for optimizing compilers. Although the comment by @Tony D below is correct: 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.

    #define DEBUG(x) do { \
      if (debugging_enabled) { std::cerr << x << std::endl; } \
    } while (0)
    

    Sometimes I also want to print the expression:

    #define DEBUG2(x) do { std::cerr << #x << ": " << x << std::endl; } while (0)
    

    In some macros, I like to include __FILE__, __LINE__ or __func__, but these are more often assertions and not simple debug macros.

    0 讨论(0)
  • 2020-12-07 08:30

    … and as addendum to all responses:

    Personally I never use macros like DEBUG to distinct debug from release code, instead I use NDEBUG which is must be defined for release builds to eliminate assert() calls (yes, I use assert() 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 when DEBUG and NDEBUG both are not defined).

    0 讨论(0)
提交回复
热议问题