在预处理器中定义可变参数函数 #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_start
、va_arg
和 va_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 时出现编译器错误=]
我正在尝试在预处理器 #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_start
、va_arg
和 va_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 时出现编译器错误=]