Как передать переменное количество аргументов в printf / sprintf


У меня есть класс, который содержит функцию "ошибки", которые форматируют текст. Я хочу принять переменное количество аргументов, а затем отформатировать их с помощью printf.

пример:

class MyClass
{
public:
    void Error(const char* format, ...);
};

метод ошибки должен принимать параметры, вызывать printf / sprintf для его форматирования, а затем что-то с ним делать. Я не хочу писать все форматирование сам, поэтому имеет смысл попробовать и выяснить, как использовать существующее форматирование.

7 67

7 ответов:

плохо

void Error(const char* format, ...)
{
    char dest[1024 * 16];
    va_list argptr;
    va_start(argptr, format);
    vsprintf(dest, format, argptr);
    va_end(argptr);
    printf(dest);
}

этот код не так хорошо. Он использует буфер символов фиксированного размера, который может привести к ошибке переполнения буфера, если строка патологически длинная. Произвольный большой 1024*16 размер должен установить флаг в твоей голове. Кроме того,printf звонок может столкнуться с проблемами, если dest в конечном итоге содержит коды форматирования. Лучше бы было printf("%s", dest). Но еще лучше было бы использовать vprintf или vfprintf:

хороший

void Error(const char* format, ...)
{
    va_list argptr;
    va_start(argptr, format);
    vfprintf(stderr, format, argptr);
    va_end(argptr);
}

если вы хотите манипулировать строкой перед ее отображением и действительно хотите, чтобы она сначала хранилась в буфере, пожалуйста, используйте vsnprintf вместо vsprintf. vsnprintf предотвратит случайную ошибку переполнения буфера.

посмотрите на vsnprintf, как это будет делать то, что вы хотитеhttp://www.cplusplus.com/reference/clibrary/cstdio/vsprintf/

сначала вам нужно будет инициализировать массив arg va_list, а затем вызвать его.

пример из этой ссылке: /* пример vsprintf */

#include <stdio.h>
#include <stdarg.h>

void Error (char * format, ...)
{
  char buffer[256];
  va_list args;
  va_start (args, format);
  vsnprintf (buffer, 255, format, args);


  //do something with the error

  va_end (args);
}

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

#include <sstream>
#include <boost/format.hpp>
#include <iostream>
using namespace std;

class formatted_log_t {
public:
    formatted_log_t(const char* msg ) : fmt(msg) {}
    ~formatted_log_t() { cout << fmt << endl; }

    template <typename T>
    formatted_log_t& operator %(T value) {
        fmt % value;
        return *this;
    }

protected:
    boost::format                fmt;
};

formatted_log_t log(const char* msg) { return formatted_log_t( msg ); }

// use
int main ()
{
    log("hello %s in %d-th time") % "world" % 10000000;
    return 0;
}

в следующем примере показаны возможные ошибки с многоточиями:

int x = SOME_VALUE;
double y = SOME_MORE_VALUE;
printf( "some var = %f, other one %f", y, x ); // no errors at compile time, but error at runtime. compiler do not know types you wanted
log( "some var = %f, other one %f" ) % y % x; // no errors. %f only for compatibility. you could write %1% instead.

Я должен был прочитать больше о существующих вопросах в Stack overflow.

C++ передача переменного числа аргументов на подобный вопрос. Майк F имеет следующее объяснение:

нет никакого способа вызова (например) printf не зная, сколько аргументов вы переходите к нему, если вы хотите чтобы попасть в непослушный и не портативный трюки.

обычно используемое решение состоит в том, чтобы всегда предлагаем альтернативную форму функции с переменным числом аргументов, так е есть vprintf, который занимает va_list на месте из.... Этот... версии есть только обертки вокруг версий va_list.

Это именно то, что я искал. Я выполнил тестовую реализацию следующим образом:

void Error(const char* format, ...)
{
    char dest[1024 * 16];
    va_list argptr;
    va_start(argptr, format);
    vsprintf(dest, format, argptr);
    va_end(argptr);
    printf(dest);
}

вы ищете variadic функции. printf () и sprintf () являются вариативными функциями - они могут принимать переменное число аргументов.

Это влечет за собой в основном следующие шаги:

  1. первый параметр должен дать некоторое представление о количестве параметров, которые следуют. Поэтому в printf () параметр "format" дает это указание - если у вас есть 5 спецификаторов формата, то он будет искать еще 5 аргументов (всего 6 аргументы.) Первый аргумент может быть целым числом (например, " myfunction(3, a, b, c)", где "3" означает "3 аргумента)

  2. затем выполните цикл и извлеките каждый последующий аргумент, используя va_start () и т. д. функции.

есть много учебников о том, как это сделать - удачи!

простой пример ниже. Обратите внимание, что вы должны пройти в более крупный буфер и проверить, был ли буфер достаточно большим или нет

void Log(LPCWSTR pFormat, ...) 
{
    va_list pArg;
    va_start(pArg, pFormat);
    char buf[1000];
    int len = _vsntprintf(buf, 1000, pFormat, pArg);
    va_end(pArg);
    //do something with buf
}

взгляните на Пример http://www.cplusplus.com/reference/clibrary/cstdarg/va_arg/, они передают количество аргументов в метод, но вы можете оммить это и соответствующим образом изменить код (см. Пример).