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

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

我正在编写一些使用以下宏的 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],尽管它不会抑制任何 space 字符,因此输出仍然会有两个冗余 space消息末尾的字符。可能你不在乎这些。

如果你知道 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 格式:

    After the %, the following appear in sequence:

    … — An optional precision that gives the minimum number of digits to appear for the d, i, o, u, x, and X conversions, …. The precision takes the form of a period (.) followed either by an asterisk * (described later) or by an optional decimal integer; if only the period is specified, the precision is taken as zero.

    The conversion specifiers and their meanings are:

    d,i The int argument is converted to signed decimal in the style [−]dddd. The precision specifies the minimum number of digits to appear; if the value being converted can be represented in fewer digits, it is expanded with leading zeros. The default precision is 1. The result of converting a zero value with a precision of zero is no characters.