简体   繁体   中英

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:

  • Only one call must be made - but it can be either to printf() or vprintf() .
  • One can use a large string buffer, sprintf() the prefix into it, then sprintf() the original arguments afterwards; but that's also not what I mean.
  • I don't mind using compiler-specific code - but if you suggest that, please try covering more than a single compiler on more than a single platform - and especially GCC or clang on GNU/Linux with an AMD64 processor.

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;
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM