如何创建带有可变参数列表的仅调试函数? 就像 printf()

发布于 2024-07-04 06:55:01 字数 231 浏览 7 评论 0原文

我想使用与 printf 相同的参数创建一个调试日志记录函数。 但在优化构建期间可以被预处理器删除。

例如:

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

我研究过可变参数宏,但它们并非在所有平台上都可用。 gcc 支持它们,msvc 不支持。

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.

For example:

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

I've looked at variadic macros but those aren't available on all platforms. gcc supports them, msvc does not.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

誰ツ都不明白 2024-07-11 06:55:02

消除可变参数函数的另一种有趣方法是:

#define function sizeof

Another fun way to stub out variadic functions is:

#define function sizeof
时间海 2024-07-11 06:55:02

@CodingTheWheel:

您的方法有一个小问题。 考虑这样的调用:

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

This 在调试版本中工作正常,但在发布版本中它将扩展为:

("x=%d", x);

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

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

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

    <前><代码>0 && XTrace

现在发布版本将扩展为:

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

并且任何像样的优化器都会丢弃整个事情,因为短路评估会阻止 & 之后的任何事情。 & 从被处决开始。

当然,正如我写下最后一句话一样,我意识到也许原始形式也可能被优化掉,并且在出现副作用的情况下,例如作为参数传递给 XTrace 的函数调用,它可能是一个更好的解决方案,因为它会确保调试版本和发布版本的行为相同。

@CodingTheWheel:

There is one slight problem with your approach. Consider a call such as

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

This works fine in the debug build, but in the release build it will expand to:

("x=%d", x);

Which is perfectly legitimate C and will compile and usually run without side-effects but generates unnecessary code. The approach I usually use to eliminate that problem is:

  1. Make the XTrace function return an int (just return 0, the return value doesn't matter)

  2. Change the #define in the #else clause to:

    0 && XTrace
    

Now the release version will expand to:

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

and any decent optimizer will throw away the whole thing since short-circuit evaluation would have prevented anything after the && from ever being executed.

Of course, just as I wrote that last sentence, I realized that perhaps the original form might be optimized away too and in the case of side effects, such as function calls passed as parameters to XTrace, it might be a better solution since it will make sure that debug and release versions will behave the same.

深居我梦 2024-07-11 06:55:02

在 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 的方式大致相同。

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.

深海少女心 2024-07-11 06:55:02

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

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

任何不提供它的平台都不是标准的 C 实现(或者非常非常旧)。 对于这些,您必须使用可变参数:

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

What platforms are they not available on? stdarg is part of the standard library:

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

Any platform not providing it is not a standard C implementation (or very, very old). For those, you will have to use varargs:

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

秋千易 2024-07-11 06:55:02

这种功能的部分问题在于它通常需要
可变参数宏。 这些是最近标准化的(C99),并且很多
旧的 C 编译器不支持该标准,或者有自己的特殊工作
大约。

下面是我编写的一个调试头,它有几个很酷的功能:

  • 支持调试宏的 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_ */

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_ */
清秋悲枫 2024-07-11 06:55:02

看看这个帖子:

它应该回答您的问题。

Have a look at this thread:

It should answer your question.

三生路 2024-07-11 06:55:02

这就是我使用的:

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 标志关闭时,它在运行时绝对不需要任何成本。

This is what I use:

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 */
}

which costs absolutely nothing at run-time when the _DEBUG_LOG flag is turned off.

与风相奔跑 2024-07-11 06:55:02

这是用户答案的​​ 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:检索最后一个错误代码

This is a TCHAR version of user's answer, so it will work as ASCII (normal), or Unicode mode (more or 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
}

I say, "more or less", because it won't automatically convert ASCII string arguments to WCHAR, but it should get you out of most Unicode scrapes without having to worry about wrapping the format string in TEXT() or preceding it with L.

Largely derived from MSDN: Retrieving the Last-Error Code

请别遗忘我 2024-07-11 06:55:02

不完全是问题中所问的内容。 但是这段代码对于调试目的很有帮助,它将打印每个变量的值及其名称。 这完全与类型无关,并且支持可变数量的参数。
甚至可以很好地显示 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 

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 
ˇ宁静的妩媚 2024-07-11 06:55:02

今天遇到这个问题,我的解决方案是以下宏:

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

Having come across the problem today, my solution is the following macro:

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

You can then call the function like this:

    int value = 42;
    DLog(L"The answer is: %d\n", value);
埖埖迣鎅 2024-07-11 06:55:01

我仍然采用旧方法,定义一个宏(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

嗯,这可以清理很多,但这是基本思想。

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.

拥抱我好吗 2024-07-11 06:55:01

这就是我在 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,编译器根本不会为该行生成任何代码。

This is how I do debug print outs in C++. Define 'dout' (debug out) like this:

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

In the code I use 'dout' just like 'cout'.

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

If the preprocessor replaces 'dout' with '0 && cout' note that << has higher precedence than && and short-circuit evaluation of && makes the whole line evaluate to 0. Since the 0 is not used the compiler generates no code at all for that line.

生生漫 2024-07-11 06:55:01

这是我用 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" ));

您所需要记住的是在调用调试函数时使用双括号,并且整行将在非调试代码中被删除。

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.

南街九尾狐 2024-07-11 06:55:01

啊,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);
}

然后将整个内容包装在宏中。

Ah, vsprintf() was the thing I was missing. I can use this to pass the variable argument list directly to 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);
}

Then wrap the whole thing in a macro.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文