Comment passer la variable nombre 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 et ensuite les formater en utilisant printf.

exemple:

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

la méthode D'erreur doit prendre en compte les paramètres, appeler printf/sprintf pour le formater et ensuite faire quelque chose avec. Je ne veux pas écrire tout le formatage moi-même donc il est logique d'essayer de comprendre comment utiliser le la mise en forme existante.

67
demandé sur hippietrail 2009-06-29 07:11:10

7 réponses

Mauvais

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

ce code n'est pas très bon. Il utilise un tampon de caractères de taille fixe qui peut conduire à une erreur de dépassement de tampon si la chaîne est pathologiquement longue. La grande taille arbitraire 1024*16 devrait déclencher un drapeau dans votre tête. En outre, l'appel printf pourrait rencontrer des problèmes si dest finit par contenir des codes de formatage. Mieux vaut printf("%s", dest) . Mais encore mieux serait d'utiliser vprintf ou vfprintf :

bon

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 avez vraiment besoin qu'elle soit stockée dans un tampon d'abord, s'il vous plaît utiliser vsnprintf au lieu de vsprintf . vsnprintf empêchera une erreur accidentelle de débordement de tampon.

125
répondu John Kugelman 2013-08-28 17:41:24

regardez vsnprintf car cela fera ce que vous voulez http://www.cplusplus.com/reference/clibrary/cstdio/vsprintf /

vous devrez d'abord initialiser le tableau va_list arg, puis l'appeler.

exemple tiré de ce lien: / * vsprintf exemple * /

#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);
}
30
répondu Lodle 2009-06-29 04:36:33

utiliser des fonctions avec les ellipses n'est pas très sûr. Si la performance n'est pas critique pour la fonction log envisager d'utiliser la surcharge de l'opérateur comme dans le format boost::. 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'échantillon suivant montre des erreurs possibles avec des 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
répondu Kirill V. Lyadvinsky 2009-06-29 07:44:57

j'aurais dû lire plus sur les questions existantes dans stack overflow.

C++ en Passant Nombre Variable d'Arguments est une question semblable. Mike F A l'explication suivante:

Il n'y a aucun moyen de l'appel (par exemple) printf sans savoir combien d'arguments tu passes devant, à moins que tu ne veuilles pour entrer dans vilain et non portable ficelles.

Le généralement utilisé la solution est de toujours fournir une forme alternative de vararg fonctions printf a vprintf qui met en place une va_list de la .... Le. .. les versions sont justes enroulement autour des versions va_list.

C'est exactement ce que je cherchais. J'ai effectué un test d'implémentation 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
répondu user5722 2017-05-23 12:02:37

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, alors il va chercher 5 plus d'arguments (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 boucle et récupère chaque argument successif, en utilisant le va_start() etc. fonction.

Il ya beaucoup de tutoriels sur la façon de faire cela - bonne chance!

3
répondu poundifdef 2009-06-29 03:17:13

exemple Simple ci-dessous. Remarque vous devez passer dans un tampon plus grand, et de tester pour voir si le tampon a été assez importante 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
répondu DougN 2009-06-29 03:17:12

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

0
répondu stefanB 2009-06-29 03:16:28