你如何创build一个带有可变参数列表的仅debugging函数? 像printf()

我想使用与printf相同的参数进行debugging日志loggingfunction。 但是可以在优化版本中被预处理器删除。

例如:

 Debug_Print("Warning: value %d > 3!\n", value); 

我已经看过可变macros,但是这些在所有平台上都不可用。 gcc支持他们, msvc不。

我仍旧按照以前的方式来做,通过定义一个与可变参数列表的无操作或函数调用相关的macros(XTRACE,见下)。 在内部调用vsnprintf,以便保持printf语法:

 #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); } 

那么一个典型的#ifdef开关:

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

那么可以清理很多,但这是基本的想法。

这是我如何在C ++中debugging打印输出。 定义'dout'(debugging出)像这样:

 #ifdef DEBUG #define dout cout #else #define dout 0 && cout #endif 

在代码中,我使用“dout”就像“cout”。

 dout << "in foobar with x= " << x << " and y= " << y << '\n'; 

如果预处理器用'0 && cout'代替'dout',那么<<优先级高于&&并且&&短路评估使得整行评估为0.由于不使用0,所以编译器根本不产生任何代码为那条线。

这是我在C / C ++中做的事情。 首先,你写了一个使用可变参数的函数(参见Stu的文章中的链接)。 然后做这样的事情:

 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" )); 

所有你必须记住的是在调用debugging函数的时候使用double-parens,并且在非DEBUG代码中整行将被删除。

@CodingTheWheel:

你的方法有一个小问题。 考虑一个像这样的电话

 XTRACE("x=%d", x); 

这在debugging版本中工作正常,但在发布版本中,它将扩展到:

 ("x=%d", x); 

这是完全合法的C,将编译,通常运行没有副作用,但生成不必要的代码。 我通常用来消除这个问题的方法是:

  1. 使XTrace函数返回一个int(只返回0,返回值无关紧要)

  2. 将#else子句中的#define更改为:

     0 && XTrace 

现在发行版本将扩展到:

 0 && XTrace("x=%d", x); 

任何像样的优化器都会抛弃整个事情,因为短路评估会阻止&&以后的任何事情的执行。

当然,正如我写了最后一句话时,我意识到也许原来的forms也许会被优化掉,而在副作用的情况下,比如函数调用作为parameter passing给XTrace,这可能是一个更好的解决scheme,因为它会确保debugging版本和发行版本的行为相同。

另一个有趣的方法来存储可变参数函数是:

 #define function sizeof 

这种function的一部分问题是它经常需要可变macros。 这些都是最近标准化的(C99),很多旧的C编译器都不支持这个标准,或者有自己特殊的工作。

下面是我写的一个debugging头,有几个很酷的function:

  • 支持debuggingmacros的C99和C89语法
  • 根据函数参数启用/禁用输出
  • 输出到文件描述符(文件io)

注意:出于某种原因,我有一些轻微的代码格式问题。

 #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_ */ 

在C ++中,您可以使用stream式运算符来简化事情:

 #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 

并使用它像:

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

然后你就可以实现自定义的类跟踪输出,就像输出到std::cout

啊,vsprintf()是我错过的东西。 我可以使用这个将可变参数列表直接传递给printf():

 #include <stdarg.h> #include <stdio.h> void DBG_PrintImpl(char * format, ...) { char buffer[256]; va_list args; va_start(args, format); vsprintf(buffer, format, args); printf("%s", buffer); va_end(args); } 

然后把整个东西包装在一个macros中。

看看这个线程:

  • 如何使可变macros(可变数量的参数)

它应该回答你的问题。

哪些平台是不可用的? stdarg是标准库的一部分:

http://www.opengroup.org/onlinepubs/009695399/basedefs/stdarg.h.html

任何不提供它的平台都不是标准的C实现(或者非常非常古老)。 对于那些,你将不得不使用可变参数:

http://opengroup.org/onlinepubs/007908775/xsh/varargs.h.html

今天遇到这个问题,我的解决办法是下面这个macros:

  static TCHAR __DEBUG_BUF[1024] #define DLog(fmt, ...) swprintf(__DEBUG_BUF, fmt, ##__VA_ARGS__); OutputDebugString(__DEBUG_BUF) 

你可以这样调用函数:

  int value = 42; DLog(L"The answer is: %d\n", value); 

这是我使用的:

 inline void DPRINTF(int level, char *format, ...) { # ifdef _DEBUG_LOG va_list args; va_start(args, format); if(debugPrint & level) { vfprintf(stdout, format, args); } va_end(args); # endif /* _DEBUG_LOG */ } 

当_DEBUG_LOG标志被closures时,在运行时完全没有成本。

这是用户答案的​​TCHAR版本,因此它将以ASCII( 普通 )或Unicode模式(或多或less)工作。

 #define DEBUG_OUT( fmt, ...) DEBUG_OUT_TCHAR( \ TEXT(##fmt), ##__VA_ARGS__ ) #define DEBUG_OUT_TCHAR( fmt, ...) \ Trace( TEXT("[DEBUG]") #fmt, \ ##__VA_ARGS__ ) void Trace(LPCTSTR format, ...) { LPTSTR OutputBuf; OutputBuf = (LPTSTR)LocalAlloc(LMEM_ZEROINIT, \ (size_t)(4096 * sizeof(TCHAR))); va_list args; va_start(args, format); int nBuf; _vstprintf_s(OutputBuf, 4095, format, args); ::OutputDebugString(OutputBuf); va_end(args); LocalFree(OutputBuf); // tyvm @sam shaw } 

我会说“或多或less”,因为它不会自动将ASCIIstring参数转换为WCHAR,但是它应该让您摆脱大多数Unicode刮擦,而不必担心将格式string封装在TEXT()中或在L之前。

大部分源自MSDN:检索最后错误代码