Повторное использование аргумента переменной функции не работает - PullRequest
12 голосов
/ 16 февраля 2012

У меня есть функция, которая пытается записывать данные в консоль, а также в файл журнала, но она не работает.Второе использование аргумента переменной длины дает мусор, записанный на консоль.Есть идеи?

    void logPrintf(const char *fmt, ...) {
        va_list ap;    // log to logfile
        va_start(ap, fmt);
        logOpen;
        vfprintf(flog, fmt, ap);
        logClose;
        va_end(ap);
        va_list ap2;   // log to console
        va_start(ap2, fmt);
        printf(fmt, ap2);
        va_end(ap2);
    }

Ответы [ 3 ]

11 голосов
/ 01 января 2017

Исходный код не работает, потому что он пытается использовать printf() там, где ему нужно использовать vprintf().Принимая сомнительные точки, такие как операторы logOpen и logClose по номинальной стоимости (учитывая обозначения, предположительно, они являются макросами, которые открывают и закрывают поток файлов flog), код должен быть:1008 * Нет особого требования использовать две отдельные va_list переменные;вполне нормально использовать один и тот же дважды , если вы используете va_end(), прежде чем снова использовать va_start().

void logPrintf(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    logOpen;
    vfprintf(flog, fmt, ap);
    logClose;
    va_end(ap);
    va_start(ap, fmt);
    vprintf(fmt, ap);
    va_end(ap);
}

Когда значение va_list передается вдругая функция (vfprintf() и vprintf() в этом коде), вы должны предположить, что она больше не может использоваться в текущей функции.На него можно только позвонить va_end().

В этом коде нет необходимости вводить va_copy().Это работает, но это не нужно.Вам нужно va_copy() в других обстоятельствах, например, когда вашей функции передается va_list, и вам нужно дважды обработать список:

void logVprintf(const char *fmt, va_list args1)
{
    va_list args2;
    va_copy(args2, args1);
    logOpen;
    vfprintf(flog, fmt, args1);
    logClose;
    vprintf(fmt, args2);
    va_end(args2);
}

Обратите внимание, что в этом коде это ответственность вызывающего кодапозвонить va_end() на args1.Действительно, стандарт гласит:

Каждый вызов макросов va_start и va_copy должен сопоставляться с соответствующим вызовом макроса va_end в той же функции.

Поскольку функция logVprintf() не вызывает ни va_start, ни va_copy для инициализации args1, она не может законно вызвать va_end для args1.С другой стороны, стандарт требует, чтобы он вызывал va_end для args2.

Функция logPrintf() может быть реализована в виде logVprintf() сейчас:

void logPrintf(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    logVprintf(fmt, args);
    va_end(args);
}

Эта структура - операционная функция, которая принимает va_list и функция покрытия, которая принимает многоточие (переменные аргументы) и передает их в операционную функцию после преобразования в va_list - часто является хорошим способом работы.Рано или поздно вам обычно требуется версия с аргументом va_list.

1 голос
/ 16 февраля 2012

Обновите свой компилятор, это больше похоже на C ++:

template <typename... Args>
void logPrintf(const char *fmt, Args&&... args) {
    logOpen;
    fprintf(flog, fmt, args...);
    logClose;

    printf(fmt, args...);
}

Хотя, конечно, было бы неплохо предложить безопасные версии printf и fprintf.

0 голосов
/ 16 февраля 2012

Я думаю, что этот способ имеет больше смысла:

void logPrintf(const char *fmt, ...) {
        va_list ap;    // log to logfile
        va_start(ap, fmt);
        logOpen;
        vfprintf(flog, fmt, ap); //logfile
         printf(fmt, ap); //console
        logClose;
        va_end(ap);
    }
...