我想实现一个可变参数函数,它的行为类似于printf,只是它打印了一些前缀。例如,假设我希望前缀为time(0)
的值。如果我打电话:
wrapped_printf("Hello, world %d", 5678);
我会期待这样的东西:
1571441246 Hello, world 5678
作为输出。
显然,替换格式字符串没什么大不了的;是可变参数业务给我带来了麻烦。我应该将其实现为...
函数吗?拿va_list
?我如何在前面加上我的额外参数?
这就是我现在所拥有的。它编译并运行(它甚至是有效的 C89...(,但额外的参数被搞砸了。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
int wrapped_printf(const char* format_str, ...)
{
static const char* const prefix = "%d ";
static const size_t prefix_length = 3;
va_list ap;
size_t format_string_length = strlen(format_str);
char* const prefixed_format_str = malloc(format_string_length + prefix_length + 2);
/* 1 for the trailing ' ' and 1 for a line break */
if (prefixed_format_str == NULL) { exit(EXIT_FAILURE); }
strncpy(prefixed_format_str, prefix, prefix_length);
strncpy(prefixed_format_str + prefix_length, format_str, format_string_length);
prefixed_format_str[prefix_length + format_string_length] = 'n';
prefixed_format_str[prefix_length + format_string_length + 1] = ' ';
va_start(ap, format_str);
return printf(
prefixed_format_str,
(int) time(0),
ap);
va_end(ap);
}
int main()
{
wrapped_printf("Hello world %dn", 5678);
return EXIT_SUCCESS;
}
看到它在科利鲁失败了。
笔记:
- 只需拨打一个电话 - 但它可以是
printf()
或vprintf()
. - 可以使用一个大的字符串缓冲区,将前缀
sprintf()
到其中,然后sprintf()
原始参数;但这也不是我的意思。 - 我不介意使用特定于编译器的代码 - 但如果你建议这样做,请尝试在多个平台上覆盖多个编译器 - 尤其是带有 AMD64 处理器的 GNU/Linux 上的 GCC 或 clang。
完全不可能在普通 C 中将参数附加到可移植的va_list
。这是肯定可以做到的,但它需要每个体系结构以及编译器和调用约定的编译器级向导。
对于可移植的方式,像libffi这样的库和格式字符串的解析器...
我建议,如果可能的话,如果你有幸使用C99+,你可以改用宏;那么你可以用编译时字符串串联在格式字符串前面"%d "
,并在参数中添加数字。但是格式需要是字符串文字。
或者,如果您确实需要使用函数,那么我不会受到不对前缀后跟这些前缀单独使用printf
的限制 -输出将是行缓冲或完全缓冲的,因此不太可能有任何区别。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#define wrapped_printf_macro(f_, ...)
printf("%lld " f_, (long long)time(0), __VA_ARGS__)
int wrapped_printf(const char* format_str, ...)
{
static const char* const prefix = "%d ";
static const size_t prefix_length = 3;
va_list ap;
printf("%lld ", (long long int)time(0));
va_start(ap, format_str);
vprintf(format_str, ap);
va_end(ap);
}
int main()
{
wrapped_printf_macro("Hello world %dn", 5678);
wrapped_printf("Hello world %dn", 5678);
return EXIT_SUCCESS;
}