93 votes

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

J'ai une classe qui contient une "erreur" fonction qui permet de formater 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, ...);
};

L'Erreur de la méthode dans les paramètres d'appel de printf/sprintf de le formater et de faire quelque chose avec elle. Je ne veux pas écrire toute la mise en forme de moi-même si cela fait sens pour essayer de comprendre comment utiliser la mise en forme existante.

172voto

John Kugelman Points 108754

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 si bon. Il utilise une taille fixe de caractères de la mémoire tampon qui peut conduire à un dépassement de la mémoire tampon d'erreur si la chaîne est pathologiquement long. L'arbitraire de grandes 1024*16 de la taille devrait être un drapeau dans votre tête. Aussi, l' printf appel pourrait rencontrer des problèmes si dest finit contenant des codes de mise en forme. Mieux, il s' 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 vraiment besoin de le stocker dans une mémoire tampon d'abord, veuillez veuillez veuillez utiliser vsnprintf au lieu de vsprintf. vsnprintf permettra d'éviter un accident débordement de la mémoire tampon d'erreur.

46voto

Lodle Points 5070

jetez un oeil à vsnprintf que cela va faire ce ya souhaitez http://www.cplusplus.com/reference/clibrary/cstdio/vsprintf/

vous aurez à init de la va_list arg tableau d'abord, puis de l'appeler.

Exemple à partir 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);
}

4voto

poundifdef Points 6005

Vous êtes à la recherche pour les variadic fonctions. printf() et sprintf() sont variadic fonctions - ils peuvent accepter un nombre variable d'arguments.

Cela comporte essentiellement les étapes suivantes:

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

  2. Puis parcourir et récupérer chaque argument, à l'aide de la va_start (), etc. fonctions.

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

2voto

DougN Points 1768

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
}

1voto

Kirill V. Lyadvinsky Points 47627

Utilisation de fonctions avec le bouton de sélection n'est pas très sûr. Si la performance n'est pas critique pour la fonction de journal d'envisager l'utilisation de la surcharge d'opérateur comme dans boost::format. Vous pouvez é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 montre les erreurs possibles avec le bouton de sélection:

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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X