Reputation: 131986
I want to implement a variadic function which behaves like printf, except that it prints some prefix. For example, let's say I want the prefix to be the value of time(0)
. if I call:
wrapped_printf("Hello, world %d", 5678);
I'll expect something like:
1571441246 Hello, world 5678
as the output.
Obviously, replacing the format string is not a big deal; it's the variadic business that's giving me trouble. Should I implement this as a function taking ...
? Taking a va_list
? And how do I prepend my extra arguments?
This is what I have right now. It compiles and runs (it's valid C89 even...), but the extra arguments get messed up.
#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 '\0' 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] = '\0';
va_start(ap, format_str);
return printf(
prefixed_format_str,
(int) time(0),
ap);
va_end(ap);
}
int main()
{
wrapped_printf("Hello world %d\n", 5678);
return EXIT_SUCCESS;
}
See it failing on Coliru.
Notes:
printf()
or vprintf()
. sprintf()
the prefix into it, then sprintf()
the original arguments afterwards; but that's also not what I mean.Upvotes: 0
Views: 584
Reputation: 133998
It is totally not possible to prepend arguments to a va_list
portably in plain C. It can be done for sure, but it would require compiler-level wizardry for each architecture and compiler and calling-convention.
For a portable way, a library like libffi and a parser for the format strings...
I suggest that if possible you'd go for a macro instead, if you're lucky enough to be on C99+; then you can prepend "%d "
to the format string with compile-time string catenation and add the number in the arguments rather easily. But the format need to be a string literal for this.
Or if you really need to use a function then I don't get the restriction of not using printf
separately for the prefix followed by these - the output would be line-buffered or fully-buffered so there would not likely be any difference.
#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 %d\n", 5678);
wrapped_printf("Hello world %d\n", 5678);
return EXIT_SUCCESS;
}
Upvotes: 1