einpoklum
einpoklum

Reputation: 131986

How to prepend formatted data to an printf() call

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:

Upvotes: 0

Views: 584

Answers (1)

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

Related Questions