如何向 printf/sprintf 传递可变数量的参数

我有一个类,它包含一个“ error”函数,用于格式化某些文本。我想接受数量可变的参数,然后使用 printf 格式化它们。

例如:

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

Error 方法应该接受参数,调用 printf/sprintf 格式化它,然后对它进行处理。我不想自己编写所有的格式,所以尝试并弄清楚如何使用现有的格式是有意义的。

137012 次浏览

have a look at vsnprintf as this will do what ya want http://www.cplusplus.com/reference/clibrary/cstdio/vsprintf/

you will have to init the va_list arg array first, then call it.

Example from that link: /* 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);
}

I should have read more on existing questions in stack overflow.

C++ Passing Variable Number of Arguments is a similar question. Mike F has the following explanation:

There's no way of calling (eg) printf without knowing how many arguments you're passing to it, unless you want to get into naughty and non-portable tricks.

The generally used solution is to always provide an alternate form of vararg functions, so printf has vprintf which takes a va_list in place of the .... The ... versions are just wrappers around the va_list versions.

This is exactly what I was looking for. I performed a test implementation like this:

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

Have a look at the example http://www.cplusplus.com/reference/clibrary/cstdarg/va_arg/, they pass the number of arguments to the method but you can ommit that and modify the code appropriately (see the example).

Simple example below. Note you should pass in a larger buffer, and test to see if the buffer was large enough or not

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
}

You are looking for variadic functions. printf() and sprintf() are variadic functions - they can accept a variable number of arguments.

This entails basically these steps:

  1. The first parameter must give some indication of the number of parameters that follow. So in printf(), the "format" parameter gives this indication - if you have 5 format specifiers, then it will look for 5 more arguments (for a total of 6 arguments.) The first argument could be an integer (eg "myfunction(3, a, b, c)" where "3" signifies "3 arguments)

  2. Then loop through and retrieve each successive argument, using the va_start() etc. functions.

There are plenty of tutorials on how to do this - good luck!

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

If you want to manipulate the string before you display it and really do need it stored in a buffer first, use vsnprintf instead of vsprintf. vsnprintf will prevent an accidental buffer overflow error.

Using functions with the ellipses is not very safe. If performance is not critical for log function consider using operator overloading as in boost::format. You could write something like this:

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

The following sample demonstrates possible errors with ellipses:

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.