可变宏调用 fprintf:如何向 __VA_ARGS__ 添加参数?
Variadic Macro calling fprintf: how to add arguments to __VA_ARGS__?
我有两个宏:
#define LogFunction(str) fprintf(stdout, "%s: %s\n",__FUNCTION__,(str))
#define LogPrintf(f_, ...) fprintf(stdout, (f_), ##__VA_ARGS__)
所以我可以这样使用它们:
void MyFunction()
{
int N=4;
LogFunction("START"); // Outputs "MyFunction: START"
LogPrintf("N=%d\n", N); // Outputs "N=4"
}
我想改成
- 在 LogPrintf 的开头添加 FUNCTION,因为它在 LogFunction
中
- 在LogPrintf的末尾加上"\n"不用自己记着放
所以最后我可以只用一个宏来输出。
我试图了解 Appending to __VA_ARGS__ 是否有用,但我承认我不明白它是否与我的情况有关:(
谢谢。
为什么不分 3 步完成?
#define LogPrintf(f_, ...) do { fprintf(stdout, "%s: ",__FUNCTION__); \
fprintf(stdout, (f_), ##__VA_ARGS__); \
fprintf(stdout,"\n"); } while(0)
这会打印 3 次,但至少它很简单并且可以满足您的需求。 do
while(0)
技巧确保这是一个唯一的块(当使用 if
不带大括号时)并且需要分号。
如果您愿意将 LogPrintf
的第一个参数作为字符串文字,那么您应该能够使用字符串连接来实现您的 objective:
// Assumes f_ always corresponds to a string literal:
#define LogPrintf(f_, ...) fprintf(stdout, "%s: " f_ "\n", __FUNCTION__, ##__VA_ARGS__)
但是请注意,在标准 C 中,LogPrintf
宏至少需要 两个 个参数,##
没有位置。我把它放在这里只是因为你在你的原始代码中使用它。
但是,如果您必须接受字符串文字以外的格式字符串表达式,那么最简单的替代方法是执行多个 I/O 调用,另一个答案也表明:
#define LogPrintf(f_, ...) do { \
fprintf(stdout, "%s: ", __FUNCTION__); \
fprintf(stdout, (f_), ##__VA_ARGS__); \
fputc('\n', stdout); \
} while (0)
请注意,在这种情况下,宏扩展为 语句(没有尾随分号),而在另一种情况下,宏扩展为 表达式。如果您想要任何 I/O 函数的 return 值,那么在这种情况下您必须为此做出特殊规定。
如果这对你也不起作用,那么最终的选择是编写并使用辅助函数,正如评论中所建议的那样:
#define LogPrintf(f_, ...) log_printf_impl(stdout, __FUNCTION__, (f_), ##__VA_ARGS__)
int log_printf_impl(FILE *f, const char *func, const char *fmt, ...) {
static const char prefix[] = "%s: ";
size_t flen = strlen(fmt);
va_list args;
int result = -1;
char *aug_fmt = malloc(sizeof(prefix) + strlen(fmt) + 1);
if (aug_fmt) {
va_start(args, fmt);
sprintf(aug_fmt, "%s%s\n", prefix, fmt);
result = vfprintf(f, aug_fmt, func, args);
va_end(args);
free(aug_fmt);
}
return result;
}
我有两个宏:
#define LogFunction(str) fprintf(stdout, "%s: %s\n",__FUNCTION__,(str))
#define LogPrintf(f_, ...) fprintf(stdout, (f_), ##__VA_ARGS__)
所以我可以这样使用它们:
void MyFunction()
{
int N=4;
LogFunction("START"); // Outputs "MyFunction: START"
LogPrintf("N=%d\n", N); // Outputs "N=4"
}
我想改成
- 在 LogPrintf 的开头添加 FUNCTION,因为它在 LogFunction 中
- 在LogPrintf的末尾加上"\n"不用自己记着放
所以最后我可以只用一个宏来输出。
我试图了解 Appending to __VA_ARGS__ 是否有用,但我承认我不明白它是否与我的情况有关:(
谢谢。
为什么不分 3 步完成?
#define LogPrintf(f_, ...) do { fprintf(stdout, "%s: ",__FUNCTION__); \
fprintf(stdout, (f_), ##__VA_ARGS__); \
fprintf(stdout,"\n"); } while(0)
这会打印 3 次,但至少它很简单并且可以满足您的需求。 do
while(0)
技巧确保这是一个唯一的块(当使用 if
不带大括号时)并且需要分号。
如果您愿意将 LogPrintf
的第一个参数作为字符串文字,那么您应该能够使用字符串连接来实现您的 objective:
// Assumes f_ always corresponds to a string literal:
#define LogPrintf(f_, ...) fprintf(stdout, "%s: " f_ "\n", __FUNCTION__, ##__VA_ARGS__)
但是请注意,在标准 C 中,LogPrintf
宏至少需要 两个 个参数,##
没有位置。我把它放在这里只是因为你在你的原始代码中使用它。
但是,如果您必须接受字符串文字以外的格式字符串表达式,那么最简单的替代方法是执行多个 I/O 调用,另一个答案也表明:
#define LogPrintf(f_, ...) do { \
fprintf(stdout, "%s: ", __FUNCTION__); \
fprintf(stdout, (f_), ##__VA_ARGS__); \
fputc('\n', stdout); \
} while (0)
请注意,在这种情况下,宏扩展为 语句(没有尾随分号),而在另一种情况下,宏扩展为 表达式。如果您想要任何 I/O 函数的 return 值,那么在这种情况下您必须为此做出特殊规定。
如果这对你也不起作用,那么最终的选择是编写并使用辅助函数,正如评论中所建议的那样:
#define LogPrintf(f_, ...) log_printf_impl(stdout, __FUNCTION__, (f_), ##__VA_ARGS__)
int log_printf_impl(FILE *f, const char *func, const char *fmt, ...) {
static const char prefix[] = "%s: ";
size_t flen = strlen(fmt);
va_list args;
int result = -1;
char *aug_fmt = malloc(sizeof(prefix) + strlen(fmt) + 1);
if (aug_fmt) {
va_start(args, fmt);
sprintf(aug_fmt, "%s%s\n", prefix, fmt);
result = vfprintf(f, aug_fmt, func, args);
va_end(args);
free(aug_fmt);
}
return result;
}