我想使用与printf
. 但是可以在优化构建期间由预处理器删除。
例如:
Debug_Print("Warning: value %d > 3!\n", value);
我查看了可变参数宏,但并非在所有平台上都可用。gcc
支持他们,msvc
不支持。
我想使用与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
好吧,这可以清理很多,但这是基本的想法。
这就是我在 C++ 中调试打印输出的方式。像这样定义“dout”(调试):
#ifdef DEBUG
#define dout cout
#else
#define dout 0 && cout
#endif
在代码中,我像使用“cout”一样使用“dout”。
dout << "in foobar with x= " << x << " and y= " << y << '\n';
如果预处理器将 'dout' 替换为 '0 && cout' 请注意 << 的优先级高于 && 并且 && 的短路评估使整行评估为 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" ));
您只需要记住在调用调试函数时使用双括号,整行将在非调试代码中被删除。
啊, 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
@CodingTheWheel:
你的方法有一个小问题。考虑一个电话,例如
XTRACE("x=%d", x);
这在调试版本中工作正常,但在发布版本中它将扩展到:
("x=%d", x);
这是完全合法的 C 语言,可以编译并且通常运行没有副作用,但会生成不必要的代码。我通常用来消除这个问题的方法是:
使XTrace函数返回一个int(只返回0,返回值无所谓)
将#else 子句中的#define 更改为:
0 && XTrace
现在发布版本将扩展为:
0 && XTrace("x=%d", x);
并且任何体面的优化器都会扔掉整个事情,因为短路评估会阻止 && 之后的任何事情被执行。
当然,正如我写最后一句话时,我意识到也许原始形式也可能被优化掉,并且在副作用的情况下,例如作为参数传递给 XTrace 的函数调用,它可能是一个更好的解决方案,因为它会确保调试和发布版本的行为相同。
在 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
.
它们在哪些平台上不可用?stdarg 是标准库的一部分:
http://www.opengroup.org/onlinepubs/009695399/basedefs/stdarg.h.html
任何不提供它的平台都不是标准的 C 实现(或者非常非常古老)。对于那些,您将不得不使用可变参数:
http://opengroup.org/onlinepubs/007908775/xsh/varargs.h.html
这种功能的部分问题是它通常需要可变参数宏。这些是最近标准化的(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_ */
今天遇到这个问题,我的解决方案是以下宏:
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 .
很大程度上源自MSDN:检索最后一个错误代码
不完全是问题中的问题。但是这段代码将有助于调试目的,它将打印每个变量的值及其名称。这完全独立于类型,并支持可变数量的参数。甚至可以很好地显示 STL 的值,因为您为它们重载了输出运算符
#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...);
}
样品用途:
int main()
{
string a;
int b;
double c;
a="string here";
b = 7;
c= 3.14;
show(a,b,c);
}
输出 :
a = string here | b = 7 | c = 3.14