Как передать переменное количество аргументов printf/sprintf
У меня есть класс, который содержит функцию "error", которая будет форматировать некоторый текст. Я хочу принять переменное количество аргументов, а затем отформатировать их с помощью printf.
Пример:
class MyClass
{
public:
void Error(const char* format, ...);
};
Метод Error должен принимать параметры, вызывать printf/sprintf для его форматирования, а затем что-то делать с ним. Я не хочу писать все форматирование самостоятельно, поэтому имеет смысл попробовать и выяснить, как использовать существующее форматирование.
Ответы
Ответ 1
Bad
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
предотвратит случайную ошибку переполнения буфера.
Ответ 2
посмотрите на vsnprintf, так как это сделает то, что вам нужно http://www.cplusplus.com/reference/clibrary/cstdio/vsprintf/
вам нужно сначала инициализировать массив аргументов va_list, а затем вызвать его.
Пример из этой ссылки:
/* vsprintf example */
#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);
}
Ответ 3
Использование функций с эллипсами не очень безопасно. Если производительность не критична для функции журнала, рассмотрите возможность использования перегрузки оператора как в формате 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.
Ответ 4
Вы ищете вариативные функции. printf() и sprintf() являются вариационными функциями - они могут принимать переменное количество аргументов.
Это влечет за собой следующие шаги:
-
Первый параметр должен дать некоторое указание на количество параметров, которые следуют. Таким образом, в printf() параметр "format" дает это указание - если у вас есть 5 спецификаторов формата, тогда он будет искать еще 5 аргументов (всего 6 аргументов). Первым аргументом может быть целое число (например, "myfunction" (3, a, b, c) "where" 3 "означает" 3 аргумента "
-
Затем выполните цикл и извлеките каждый следующий аргумент, используя функции va_start() и т.д.
Есть много уроков о том, как это сделать - удачи!
Ответ 5
Мне нужно больше узнать о существующих вопросах при переполнении стека.
С++ Передача переменной Количество аргументов - аналогичный вопрос. Майк Ф. имеет следующее объяснение:
Нет способа вызова (например) printf не зная, сколько аргументов вы переходите к нему, если не хотите попасть в непослушный и непереносимый фокусы.
Обычно используемое решение всегда предоставляют альтернативную форму vararg, поэтому 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);
}
Ответ 6
Простой пример ниже. Обратите внимание, что вы должны пройти в большем буфере и проверить, достаточно ли буфера или нет.
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
}
Ответ 7
Посмотрите пример http://www.cplusplus.com/reference/clibrary/cstdarg/va_arg/, они передают количество аргументов в метод, но вы можете опустить это и изменить (см. пример).