Как добавить предварительно отформатированные данные к вызову printf () - PullRequest
0 голосов
/ 19 октября 2019

Я хочу реализовать функцию variadic, которая ведет себя как 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 '\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;
}

Посмотрите не удалось в Coliru.

Примечания:

  • Должен быть сделан только один звонок, но это может быть либо printf(), либо vprintf().
  • Можно использовать большой строковый буфер, sprintf() префикс в него, затем sprintf() исходные аргументы впоследствии;но это также не то, что я имею в виду.
  • Я не возражаю против использования кода, специфичного для компилятора, - но если вы предлагаете это, попробуйте охватить более одного компилятора на более чем одной платформе - и особенно GCC илиclang в GNU / Linux с процессором AMD64.

1 Ответ

1 голос
/ 19 октября 2019

Совершенно невозможно добавить аргументы к va_list, переносимому на простом C. Это можно сделать наверняка, но для каждой архитектуры потребуется 100% -ное волшебство на уровне компилятора и компилятор и соглашение о вызовах .

Для переносимого способа - библиотека, подобная 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 %d\n", 5678);
    wrapped_printf("Hello world %d\n", 5678);
    return EXIT_SUCCESS;
}
...