在预处理器中定义可变参数函数 #define

Defining a variable arguments function in a preprocessor #define

我正在尝试在预处理器 #define 中定义一个 debug_log 函数,以便此函数仅存在于调试模式下。 问题是我希望使用 variable_argument 函数:

#ifdef DEBUG
    #define DEBUG_ENABLED 1
#else
    #define DEBUG_ENABLED 0
#endif

#define debug_log(msg, ...)                                                                 \
        do {                                                                                \
            if (DEBUG_ENABLED) {                                                            \
                char str[300];                                                              \
                int length = -1;                                                            \
                va_list argList;                                                            \
                va_start( argList, msg );                                                   \
                length = vsnprintf(str, sizeof(str), msg, argList);                         \
                va_end( argList );                                                          \
                if (length > 0)                                                             \
                {                                                                           \
                    fprintf(stderr, "%s, %d ",__func__, __LINE__);                          \
                    fprintf(stderr, "%s", str);                                             \
                    fprintf(stderr,"\n");                                                   \
                }                                                                           \
            }                                                                               \
        } while (0)                                                                         \

编译器正在返回:

error: ‘va_start’ used in function with fixed args [build] 20 | va_start( argList, msg ); \

感谢您的帮助 ;)

您实际上并不是在此处定义函数。您正在定义一段代码,希望成为可变参数函数的一部分。当您调用此宏时,您很可能 不在 可变参数函数中,因此出现错误。

相反,在 #if 块中定义一个实际函数,并在 #else 块中定义一个类似虚拟函数的宏,它什么都不做。

#ifdef DEBUG

#define debug_log(...)  debug_log_impl(__func__, __LINE__, __VA_ARGS__)

void debug_log_impl(const char *func, int line, const char *msg, ...)
{
    char str[300];
    int length = -1;
    va_list argList;
    va_start( argList, msg );
    length = vsnprintf(str, sizeof(str), msg, argList);
    va_end( argList );
    if (length > 0)
    {
        fprintf(stderr, "%s, %d ", func, line);
        fprintf(stderr, "%s", str);
        fprintf(stderr,"\n");
    }
}

#else

#define debug_log(...) (void)0

#fi

关于为什么 (void)0 应该在 #else 情况下使用而不是空表达式的评论中有一个问题。假设您要使用 debug_log 作为逗号运算符的左操作数:

while (debug_log("iterating, x=%d",x), x>0)

根据上面的定义,如果未定义 DEBUG,则此行扩展为:

while ((void)0, x>0)

如果它是一个空表达式,它将扩展为:

while (, x>0)

语法错误。

您混淆了宏的可变参数访问和函数的可变参数访问。

宏参数列表中的...表示宏的可变参数。宏替换文本中的标识符 __VA_ARGS__ 扩展为变量参数。

函数参数列表中的...表示函数的可变参数。 va_list 类型的对象可用于使用 va_startva_argva_end 宏访问这些变量参数(如果需要,可能还有 va_copy 宏) .这些由 #include <stdarg.h>.

定义

您的 debug_log 宏不是函数,因此它不是具有可变参数的函数,因此它不能使用 va_start 等来访问这些参数。它可以做的是将宏变量参数作为一个整体传递给其他东西。对于您的示例,snprintf 将是替代您最初使用的 vsnprintf 的不错选择:

#define debug_log(msg, ...)                                                                 \
        do {                                                                                \
            if (DEBUG_ENABLED) {                                                            \
                char str[300];                                                              \
                int length = -1;                                                            \
                length = snprintf(str, sizeof(str), msg, __VA_ARGS__);                      \
                if (length > 0)                                                             \
                {                                                                           \
                    fprintf(stderr, "%s, %d ",__func__, __LINE__);                          \
                    fprintf(stderr, "%s", str);                                             \
                    fprintf(stderr,"\n");                                                   \
                }                                                                           \
            }                                                                               \
        } while (0) 

上面的表单至少需要两个参数,所以你不能用它来打印一个简单的调试消息,比如 "got here"。作为一种变通方法,可以定义没有固定参数的宏,省略 msg 参数:

#define debug_log(...)                                                                      \
        do {                                                                                \
            if (DEBUG_ENABLED) {                                                            \
                char str[300];                                                              \
                int length = -1;                                                            \
                length = snprintf(str, sizeof(str), __VA_ARGS__);                           \
                if (length > 0)                                                             \
                {                                                                           \
                    fprintf(stderr, "%s, %d ",__func__, __LINE__);                          \
                    fprintf(stderr, "%s", str);                                             \
                    fprintf(stderr,"\n");                                                   \
                }                                                                           \
            }                                                                               \
        } while (0) 

这将允许您尝试调用根本不带参数的宏,例如 debug_log();,但这将导致在调用 snprintf.[=29 时出现编译器错误=]