How do you create a debug only function that takes a variable argument list? Like printf()

后端 未结 14 2092
心在旅途
心在旅途 2020-12-23 09:44

I\'d like to make a debug logging function with the same parameters as printf. But one that can be removed by the pre-processor during optimized builds.

<
相关标签:
14条回答
  • 2020-12-23 10:04

    I still do it the old way, by defining a macro (XTRACE, below) which correlates to either a no-op or a function call with a variable argument list. Internally, call vsnprintf so you can keep the printf syntax:

    #include <stdio.h>
    
    void XTrace0(LPCTSTR lpszText)
    {
       ::OutputDebugString(lpszText);
    }
    
    void XTrace(LPCTSTR lpszFormat, ...)
    {
        va_list args;
        va_start(args, lpszFormat);
        int nBuf;
        TCHAR szBuffer[512]; // get rid of this hard-coded buffer
        nBuf = _vsnprintf(szBuffer, 511, lpszFormat, args);
        ::OutputDebugString(szBuffer);
        va_end(args);
    }
    

    Then a typical #ifdef switch:

    #ifdef _DEBUG
    #define XTRACE XTrace
    #else
    #define XTRACE
    #endif
    

    Well that can be cleaned up quite a bit but it's the basic idea.

    0 讨论(0)
  • 2020-12-23 10:06

    Here's something that I do in C/C++. First off, you write a function that uses the varargs stuff (see the link in Stu's posting). Then do something like this:

    
     int debug_printf( const char *fmt, ... );
     #if defined( DEBUG )
      #define DEBUG_PRINTF(x) debug_printf x
     #else
       #define DEBUG_PRINTF(x)
     #endif
    
     DEBUG_PRINTF(( "Format string that takes %s %s\n", "any number", "of args" ));
    

    All you have to remember is to use double-parens when calling the debug function, and the whole line will get removed in non-DEBUG code.

    0 讨论(0)
  • 2020-12-23 10:09

    Have a look at this thread:

    • How to make a variadic macro (variable number of arguments)

    It should answer your question.

    0 讨论(0)
  • 2020-12-23 10:11

    Part of the problem with this kind of functionality is that often it requires variadic macros. These were standardized fairly recently(C99), and lots of old C compilers do not support the standard, or have their own special work around.

    Below is a debug header I wrote that has several cool features:

    • Supports C99 and C89 syntax for debug macros
    • Enable/Disable output based on function argument
    • Output to file descriptor(file io)

    Note: For some reason I had some slight code formatting problems.

    #ifndef _DEBUG_H_
    #define _DEBUG_H_
    #if HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include "stdarg.h"
    #include "stdio.h"
    
    #define ENABLE 1
    #define DISABLE 0
    
    extern FILE* debug_fd;
    
    int debug_file_init(char *file);
    int debug_file_close(void);
    
    #if HAVE_C99
    #define PRINT(x, format, ...) \
    if ( x ) { \
    if ( debug_fd != NULL ) { \
    fprintf(debug_fd, format, ##__VA_ARGS__); \
    } \
    else { \
    fprintf(stdout, format, ##__VA_ARGS__); \
    } \
    }
    #else
    void PRINT(int enable, char *fmt, ...);
    #endif
    
    #if _DEBUG
    #if HAVE_C99
    #define DEBUG(x, format, ...) \
    if ( x ) { \
    if ( debug_fd != NULL ) { \
    fprintf(debug_fd, "%s : %d " format, __FILE__, __LINE__, ##__VA_ARGS__); \
    } \
    else { \
    fprintf(stderr, "%s : %d " format, __FILE__, __LINE__, ##__VA_ARGS__); \
    } \
    }
    
    #define DEBUGPRINT(x, format, ...) \
    if ( x ) { \
    if ( debug_fd != NULL ) { \
    fprintf(debug_fd, format, ##__VA_ARGS__); \
    } \
    else { \
    fprintf(stderr, format, ##__VA_ARGS__); \
    } \
    }
    #else /* HAVE_C99 */
    
    void DEBUG(int enable, char *fmt, ...);
    void DEBUGPRINT(int enable, char *fmt, ...);
    
    #endif /* HAVE_C99 */
    #else /* _DEBUG */
    #define DEBUG(x, format, ...)
    #define DEBUGPRINT(x, format, ...)
    #endif /* _DEBUG */
    
    #endif /* _DEBUG_H_ */
    
    0 讨论(0)
  • 2020-12-23 10:16

    In C++ you can use the streaming operator to simplify things:

    #if defined _DEBUG
    
    class Trace
    {
    public:
       static Trace &GetTrace () { static Trace trace; return trace; }
       Trace &operator << (int value) { /* output int */ return *this; }
       Trace &operator << (short value) { /* output short */ return *this; }
       Trace &operator << (Trace &(*function)(Trace &trace)) { return function (*this); }
       static Trace &Endl (Trace &trace) { /* write newline and flush output */ return trace; }
       // and so on
    };
    
    #define TRACE(message) Trace::GetTrace () << message << Trace::Endl
    
    #else
    #define TRACE(message)
    #endif
    

    and use it like:

    void Function (int param1, short param2)
    {
       TRACE ("param1 = " << param1 << ", param2 = " << param2);
    }
    

    You can then implement customised trace output for classes in much the same way you would do it for outputting to std::cout.

    0 讨论(0)
  • 2020-12-23 10:16

    Not exactly what's asked in the question . But this code will be helpful for debugging purposes , it will print each variable's value along with it's name . This is completely type independent and supports variable number of arguments. And can even display values of STL's nicely , given that you overload output operator for them

    #define show(args...) describe(#args,args);
    template<typename T>
    void describe(string var_name,T value)
    {
        clog<<var_name<<" = "<<value<<" ";
    }
    
    template<typename T,typename... Args>
    void describe(string var_names,T value,Args... args)
    {
        string::size_type pos = var_names.find(',');
        string name = var_names.substr(0,pos);
        var_names = var_names.substr(pos+1);
        clog<<name<<" = "<<value<<" | ";
        describe(var_names,args...);
    }
    

    Sample Use :

    int main()
    {
        string a;
        int b;
        double c;
        a="string here";
        b = 7;
        c= 3.14;
        show(a,b,c);
    }
    

    Output :

    a = string here | b = 7 | c = 3.14 
    
    0 讨论(0)
提交回复
热议问题