在printf中使用MACROS,该MACROS可以扩展为整数或无值

Using MACROS in printf that may be expanded to an integer or no value

本文关键字:MACROS 扩展 整数 printf      更新时间:2023-10-16

我正在编写一些使用以下宏的C++代码:

#if(PRINT_DEBUG_SYMBOLS)
    #define FILE_NAME __FILE__
    #define LINE_NUMBER __LINE__
#else
    #define FILE_NAME ""
    #define LINE_NUMBER 

我以以下方式使用这些宏:

SendMsg(2000, "Unable to open file %s %d", FILE_NAME, LINE_NUMBER);

如您所见,FILE_NAME扩展为文件名或基于PRINT_DEBUG SYMBOLS的空字符串。我希望LINE_NUMBER有类似的行为。它应该根据print_DEBUG_SYMBOLS打印行号或不打印行号。但是,整数没有类似空字符串的等价项,所以当PRINT_DEBUG_SYMBOLS=0时,我会打印一些随机整数。我该如何解决这个问题?

以下是SendMsg()的定义。我没有修改这个函数的灵活性。

void SendMsg(int code, char* Msg, ...)
{
    char buffer[256];
    va_list args;
    va_start(args, Msg);
    vsprintf(buffer, Msg, args);
    ostringstream line;
    line<<code<<buffer;
    printf("%s", line.str);
}

一个简单的解决方案是在非调试情况下使用#define LINE_NUMBER 0,然后使用格式%.d而不是%d:

SendMsg(2000, "Unable to open file %s %.d", FILE_NAME, LINE_NUMBER);

这将导致0不被打印[参见注释1],尽管它不会抑制任何空格字符,因此输出在消息末尾仍将有两个冗余空格字符。也许你不在乎。

如果您知道FILE_NAMELINE_NUMBER都是宏(或像__LINE__这样的伪宏),并且LINE_NUMBER是一个文字数字,那么您可以字符串化和字符串连接来生成一个字段:

#define STR_(x) #x
#define STR(x) STR_(x)
#define FILE_LINE FILE_NAME " " STR_(LINE_NUMBER)
SendMsg(2000, "Unable to open file %s", FILE_LINE);

或者你可以更复杂,同时提供格式和值宏:

// FILE_NAME and LINE_NUMBER need to be defined at some point
#if(PRINT_DEBUG_SYMBOLS)
    #define FILE_LINE FILE_NAME, LINE_NUMBER
    #define FL_FMT "%s:%d"
#else
    #define FILE_LINE ""
    #define FL_FMT "%s"
#endif
// ...
SendMsg(2000, "Unable to open file " FL_FMT, FILE_LINE);

注意事项:

  1. 摘录自C11标准,§7.21.6.1,记录*printf格式:

    %之后,以下内容按顺序出现:

    …--一种可选精度,用于为diouxX转换提供最小位数;。精度采用句点(.)的形式,后跟星号*(稍后描述)或可选的十进制整数;如果只指定了周期,则精度为零。

    转换说明符及其含义为:

    d,i int参数转换为样式为[−]dddd的带符号小数。精度指定要显示的最小位数;如果被转换的值可以用更少的数字表示,那么它将用前导零展开。默认精度为1。转换精度为零的零值的结果是没有字符。