我想使用与printf
相同的参数进行调试日志记录功能。但是在优化版本中可以被预处理器删除。如何创建一个只有调试变量参数列表的函数?像printf()
例如:
Debug_Print("Warning: value %d > 3!\n", value);
我看了复杂的宏,但这些并非适用于所有平台。 gcc
支持他们,msvc
不支持。
我想使用与printf
相同的参数进行调试日志记录功能。但是在优化版本中可以被预处理器删除。如何创建一个只有调试变量参数列表的函数?像printf()
例如:
Debug_Print("Warning: value %d > 3!\n", value);
我看了复杂的宏,但这些并非适用于所有平台。 gcc
支持他们,msvc
不支持。
通过定义一个与无操作或具有可变参数列表的函数调用相关的宏(下面的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
那么可以清理了不少,但它的基本思想。
优秀的答案,但是您应该使用** _ vsntprintf **实际上使其兼容Unicode。我添加了自己的版本,因为我需要预先安装一个字符串(如[DEBUG])。 http://vsackprint.com/zh/39186784/912236 – Orwellophile 2016-08-27 23:50:47
`_vsnprintf()`特定于特定的(与Microsoft兼容的)实现 - 并且已被弃用。 `vsnprintf()`是标准的。 – Peter 2016-08-28 00:11:42
它们没有提供哪些平台? stdarg头文件是标准库的一部分:
http://www.opengroup.org/onlinepubs/009695399/basedefs/stdarg.h.html
任何平台都没有提供它不是一个标准的C实现(或非常,非常大)。对于这些,你将不得不使用可变参数:
http://opengroup.org/onlinepubs/007908775/xsh/varargs.h.html
http://msdn.microsoft.com/en-us/library/kb57fad8(VS.71).aspx - 为了记录,这篇文章甚至向您展示了如何使用老派的可变参数作为奖励。 – Stu 2008-08-18 22:13:32
看起来像在Visual C++ 2005中引入了对可变宏的支持。 – hyperlogic 2008-08-18 22:15:41
下面是我在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"));
所有你必须要记住的是调用调试功能时,使用双括号,以及整条生产线将在非调试代码移除。
啊,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);
}
然后将整个事情包装在宏中。
另一种有趣的方式来存根出可变参数的功能是:
#define function sizeof
在C++中,你可以使用流媒体运营商把事情简单化:
#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
。
这种功能的部分问题是它通常需要 可变宏。这些都是最近标准化的(C99),旧版C编译器不支持该标准,或者有自己的特殊工作 左右。
下面是一个调试头我写的有几个很酷的功能:
注意:出于某种原因,我有一些轻微的代码格式问题。
#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_ */
使用“debug_fd”来保存文件指针而不是文件描述符似乎很奇怪;使用'debug_fp'会更传统。 – 2009-07-17 05:50:45
@CodingTheWheel:
有你的方法一个小问题。考虑一个调用,如
XTRACE("x=%d", x);
这在调试版本工作正常,但在发布版本将扩大到:
("x=%d", x);
这是完全合法的C和将编译并通常没有侧运行 - 影响,但生成不必要的代码。我通常用它来消除这一问题的方法是:
充分利用X跟踪功能返回一个int(只返回0,则返回值并不重要)
变化在#中的#define else子句到:
0 && XTrace
现在的发行版将扩大到:
0 && XTrace("x=%d", x);
和任何像样的优化器都会丢掉整个东西,因为短路评估会阻止任何东西在执行之后发生。
当然,就像我写了最后一句话时,我意识到也许最初的形式也可能被优化掉,并且在副作用的情况下,比如函数调用作为参数传递给XTrace,可能会更好解决方案,因为它将确保调试版本和发行版本的行为相同。
这是我如何在C++中调试打印输出。这样定义 'DOUT'(调试出来):
#ifdef DEBUG
#define dout cout
#else
#define dout 0 && cout
#endif
在我使用 'DOUT' 就像 'COUT' 的代码。
dout << "in foobar with x= " << x << " and y= " << y << '\n';
如果预处理器替代“DOUT”与“0 & & COUT”注意< <具有更高的优先级比& & & &和短路评价,使整条生产线计算为0。由于不使用0,因此编译器根本不会为该行生成任何代码。
碰到过今天的问题,我的解决办法是下面的宏:
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标志被关闭时,它在运行时完全没有成本。
这是用户答案的TCHAR版本,因此它将以ASCII(正常)或Unicode模式(或多或少)工作。
#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
}
我说,“或多或少”,因为它不会自动ASCII字符串参数转换为WCHAR,但它应该让你出去的大多数的Unicode擦伤,而不必担心包裹在格式字符串TEXT()或L.
斯图衍生它之前,MSVC确实支持可变参数的功能,它不支持可变参数宏。编辑:我的坏:在Visual C++ 2005中引入了对variadic宏的支持。 – hyperlogic 2008-08-18 22:15:41
另请参阅[C`#define宏用于调试打印](https://stackoverflow.com/questions/1644868/c-define-macro-for -debug印刷)。请特别注意,通常最好确保编译器编译(但优化)代码,以便代码始终被检查,因此始终是正确的。否则,bit-rot会在十年后重新激活调试宏时设置,并且您会发现它不再编译。 – 2016-04-09 23:18:05