web-dev-qa-db-fra.com

Comment passer un nombre variable d'arguments à printf / sprintf

J'ai une classe qui contient une fonction "erreur" qui formatera du texte. Je veux accepter un nombre variable d'arguments, puis les formater à l'aide de printf.

Exemple:

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

La méthode Error doit prendre en compte les paramètres, appelez printf/sprintf pour le formater puis faites quelque chose avec. Je ne veux pas écrire tout le formatage moi-même, il est donc logique d'essayer de comprendre comment utiliser le formatage existant.

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

Si vous voulez manipuler la chaîne avant de l'afficher et que vous en avez réellement besoin, elle sera d'abord stockée dans un tampon, utilisez vsnprintf au lieu de vsprintf. vsnprintf empêchera une erreur de débordement de tampon accidentelle.

141
John Kugelman

jetez un oeil à vsnprintf car cela fera ce que vous voulez http://www.cplusplus.com/reference/clibrary/cstdio/vsprintf/

vous devrez d'abord initialiser le tableau arg va_list, puis appelez-le.

Exemple à partir de ce lien:/* exemple 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);
}
31
Lodle

L'utilisation de fonctions avec les ellipses n'est pas très sûre. Si les performances ne sont pas critiques pour la fonction de journalisation, envisagez d'utiliser la surcharge de l'opérateur comme dans boost :: format. Vous pourriez écrire quelque chose comme ceci:

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

L'exemple suivant illustre les erreurs possibles avec les 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.
5

J'aurais dû en savoir plus sur les questions existantes concernant le débordement de pile.

Nombre variable d'arguments C++ est une question similaire. Mike F a l'explication suivante:

Il n'y a aucun moyen d'appeler (par exemple) printf sans connaître le nombre d'arguments que vous lui transmettez, à moins que vous ne vouliez vous lancer dans des trucs coquins et non portables.

La solution généralement utilisée est de toujours fournir une forme alternative de fonctions vararg. Par conséquent, printf a vprintf qui remplace par va_list .... Les versions ... ne sont que des wrappers autour des versions de va_list.

Ceci est exactement ce que je cherchais. J'ai effectué une implémentation de test comme ceci:

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

Vous recherchez fonctions variadiques . printf () et sprintf () sont des fonctions variadiques - elles peuvent accepter un nombre variable d'arguments.

Cela implique essentiellement ces étapes:

  1. Le premier paramètre doit donner une indication du nombre de paramètres qui suivent. Ainsi, dans printf (), le paramètre "format" donne cette indication - si vous avez 5 spécificateurs de format, il recherchera alors 5 arguments supplémentaires (pour un total de 6 arguments). Le premier argument pourrait être un entier (par exemple, "myfunction (3, a, b, c) "où" 3 "signifie" 3 arguments "

  2. Ensuite, parcourez et récupérez chaque argument successif, en utilisant les fonctions va_start (), etc.

Il y a beaucoup de tutoriels sur la façon de le faire - bonne chance!

3
poundifdef

Exemple simple ci-dessous. Notez que vous devriez passer dans un tampon plus grand et tester pour voir si le tampon était assez grand ou pas.

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
}
2
DougN

Regardez l'exemple http://www.cplusplus.com/reference/clibrary/cstdarg/va_arg/ , ils transmettent le nombre d'arguments à la méthode, mais vous pouvez l'omettre et modifier le code. de manière appropriée (voir l'exemple).

0
stefanB