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 en utilisant printf.

Exemple:

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

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

Mal

 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 un tampon de caractères de taille fixe qui peut entraîner une erreur de saturation de la mémoire tampon si la chaîne est pathologiquement longue. La grande taille arbitraire 1024*16 devrait déclencher un drapeau dans votre tête. L’appel printf peut également rencontrer des problèmes si dest contient des codes de formatage. Mieux serait printf("%s", dest) . Mais encore mieux utiliserait vprintf ou vfprintf :

Bien

 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 si vous en avez besoin, vsnprintf abord dans un tampon, veuillez s’il vous plaît utiliser vsnprintf au lieu de vsprintf . vsnprintf empêchera une erreur de dépassement de tampon accidentel.

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

vous devrez d’abord lancer le tableau va_list arg, puis l’appeler.

Exemple de ce lien: / * vsprintf exemple * /

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

L’utilisation des 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 d’opérateur comme dans boost :: format. Vous pourriez écrire quelque chose comme ceci:

 #include  #include  #include  using namespace std; class formatted_log_t { public: formatted_log_t(const char* msg ) : fmt(msg) {} ~formatted_log_t() { cout << fmt << endl; } template  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 des erreurs possibles avec des points de suspension:

 int x = SOME_VALUE; double y = SOME_MORE_VALUE; printf( "some var = %f, other one %f", y, x ); // no errors at comstack time, but error at runtime. comstackr 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. 

J’aurais dû en savoir plus sur les questions existantes dans le dépassement de stack.

C ++ Passing Variable Number of Arguments est une question similaire. Mike F a l’explication suivante:

Il n’y a aucun moyen d’appeler (par exemple) printf sans savoir combien d’arguments vous lui transmettez, à moins que vous ne souhaitiez utiliser des astuces vilaines et non portables.

La solution généralement utilisée est de toujours fournir une autre forme de fonction vararg, de sorte que printf a vprintf qui prend une va_list à la place de …. 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 celle-ci:

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

Vous recherchez des 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 parameters suivants. Donc, dans printf (), le paramètre “format” donne cette indication – si vous avez 5 spécificateurs de format, il recherchera 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 faire cela – bonne chance!

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

 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 } 

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