c++ - pro - Cómo pasar el número variable de argumentos a printf/sprintf
sprintf pro c (7)
Tengo una clase que contiene una función de "error" que formateará algún texto. Quiero aceptar una cantidad variable de argumentos y luego formatearlos usando printf.
Ejemplo:
class MyClass
{
public:
void Error(const char* format, ...);
};
El método de error debe tomar los parámetros, invocar printf / sprintf para formatearlo y luego hacer algo con él. No quiero escribir todo el formato, por lo que tiene sentido intentar averiguar cómo usar el formato existente.
Malo
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);
}
Este código no es tan bueno. Utiliza un búfer de caracteres de tamaño fijo que puede provocar un error de desbordamiento del búfer si la cadena es patológicamente larga. El tamaño arbitrario de 1024*16
debe hacer sonar una bandera en tu cabeza. Además, la llamada printf
podría tener problemas si el dest
termina conteniendo códigos de formato. Mejor sería printf("%s", dest)
. Pero aún mejor sería usar vprintf
o vfprintf
:
Bueno
void Error(const char* format, ...)
{
va_list argptr;
va_start(argptr, format);
vfprintf(stderr, format, argptr);
va_end(argptr);
}
Si desea manipular la cadena antes de mostrarla y realmente la necesita almacenada primero en un buffer, use vsnprintf
lugar de vsprintf
. vsnprintf
evitará un error accidental de desbordamiento de búfer.
Debería haber leído más sobre las preguntas existentes en desbordamiento de pila.
C ++ Pasando Variable Número de argumentos es una pregunta similar. Mike F tiene la siguiente explicación:
No hay forma de llamar (por ejemplo) printf sin saber cuántos argumentos le estás pasando, a menos que quieras entrar en trucos traviesos y no portátiles.
La solución generalmente utilizada es proporcionar siempre una forma alternativa de funciones vararg, así que printf tiene vprintf que toma una va_list en lugar de .... Las ... versiones son solo envoltorios alrededor de las versiones de va_list.
Esto es exactamente lo que estaba buscando. Realicé una implementación de prueba como esta:
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);
}
Eche un vistazo al ejemplo http://www.cplusplus.com/reference/clibrary/cstdarg/va_arg/ , pasan el número de argumentos al método, pero puede omitirlo y modificar el código de manera apropiada (vea el ejemplo).
Ejemplo simple a continuación. Tenga en cuenta que debe pasar un buffer más grande y probar para ver si el buffer fue lo suficientemente grande o no
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
}
Usar funciones con las elipses no es muy seguro. Si el rendimiento no es crítico para la función de registro, considere usar la sobrecarga del operador como en el formato boost ::. Podrías escribir algo como esto:
#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;
}
El siguiente ejemplo demuestra posibles errores con puntos suspensivos:
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.
Usted está buscando funciones variadas . printf () y sprintf () son funciones variadas: pueden aceptar un número variable de argumentos.
Esto implica básicamente estos pasos:
El primer parámetro debe dar alguna indicación del número de parámetros que siguen. Entonces, en printf (), el parámetro "formato" da esta indicación: si tiene 5 especificadores de formato, entonces buscará 5 argumentos más (para un total de 6 argumentos). El primer argumento podría ser un entero (por ejemplo, "mi función (3, a, b, c) "donde" 3 "significa" 3 argumentos)
Luego recorra y recupere cada argumento sucesivo, usando las funciones va_start () etc.
Hay muchos tutoriales sobre cómo hacer esto: ¡buena suerte!
eche un vistazo a vsnprintf ya que esto hará lo que quiera http://www.cplusplus.com/reference/clibrary/cstdio/vsprintf/
primero deberá iniciar la matriz de argumentos va_list y luego llamarla.
Ejemplo de ese enlace: / * 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);
}