C ++: comment obtenir les résultats de fprintf en tant que std :: ssortingng sans sprintf

Je travaille avec un outil UNIX open-source implémenté en C ++, et je dois changer du code pour le faire faire ce que je veux. Je voudrais faire le plus petit changement possible dans l’espoir d’obtenir mon patch accepté en amont. Les solutions qui sont implémentables en C ++ standard et ne créent pas plus de dépendances externes sont préférées.

Voici mon problème. J’ai une classe C ++ – appelons-la “A” – qui utilise actuellement fprintf () pour imprimer ses structures de données fortement formatées en un pointeur de fichier. Dans sa fonction d’impression, il appelle également de manière récursive les fonctions d’impression définies de manière identique pour plusieurs classes membres (“B” est un exemple). Il y a une autre classe C qui a un membre std :: ssortingng “foo” qui doit être défini sur les résultats print () d’une instance de A. Pensez-y comme une fonction membre to_str () pour A.

En pseudocode:

class A { public: ... void print(FILE* f); B b; ... }; ... void A::print(FILE *f) { std::ssortingng s = "stuff"; fprintf(f, "some %s", s); b.print(f); } class C { ... std::ssortingng foo; bool set_foo(std::str); ... } ... A a = new A(); C c = new C(); ... // wish i knew how to write A's to_str() c.set_foo(a.to_str()); 

Je devrais mentionner que C est assez stable, mais que A et B (et le rest des dépendants de A) sont dans un état de stream, de sorte que le moins de changements de code est nécessaire. L’interface d’impression actuelle (FILE * F) doit également être préservée. J’ai envisagé plusieurs approches pour implémenter A :: to_str (), chacune présentant des avantages et des inconvénients:

  1. Changez les appels à fprintf () en sprintf ()

    • Je n’aurais pas à réécrire les chaînes de format
    • print () pourrait être réimplémenté sous la forme: fprint (f, this.to_str ());
    • Mais je devrais allouer manuellement des char [] s, fusionner beaucoup de chaînes de caractères et finalement convertir le tableau de caractères en une chaîne std ::
  2. Essayez d’attraper les résultats de a.print () dans un stream de chaîne

    • Je devrais convertir toutes les chaînes de format en << format de sortie. Il y a des centaines de fprintf () s à convertir: – {
    • print () devrait être réécrit car il n’existe pas de méthode standard que je connaisse pour créer un stream de sortie à partir d’un descripteur de fichier UNIX (même si ce gars dit que cela peut être possible ).
  3. Utiliser la bibliothèque de formats de chaînes de Boost

    • Plus de dépendances externes. Beurk.
    • La syntaxe du format est suffisamment différente de printf () pour être agaçante:

    printf (format_str, args) -> cout << boost :: format (format_str)% arg1% arg2% etc

  4. Utilisez QSsortingng :: asprintf () de Qt

    • Une dépendance externe différente.

Alors, j’ai épuisé toutes les options possibles? Si oui, quel est selon vous le meilleur choix? Si non, qu’est-ce que j’ai oublié?

Merci.

J’utilise # 3: la librairie de format de chaîne de boost – mais je dois admettre que je n’ai jamais eu de problème avec les différences de spécifications de format.

Fonctionne comme un charme pour moi – et les dépendances externes pourraient être pires (une bibliothèque très stable)

Edité: ajout d’un exemple sur l’utilisation de boost :: format au lieu de printf:

 sprintf(buffer, "This is a ssortingng with some %s and %d numbers", "ssortingngs", 42); 

serait quelque chose comme ça avec la bibliothèque boost :: format:

 ssortingng = boost::str(boost::format("This is a ssortingng with some %s and %d numbers") %"ssortingngs" %42); 

J’espère que cela aide à clarifier l’utilisation de boost :: format

J’ai utilisé boost :: format en remplacement de sprintf / printf dans 4 ou 5 applications (écriture de chaînes formatées dans des fichiers, ou sortie personnalisée dans des fichiers journaux) et je n’ai jamais eu de problèmes de format. Il peut y avoir des spécificateurs de format (plus ou moins obscurs) qui sont différents – mais je n’ai jamais eu de problème.

En revanche, j’avais des spécifications de format que je ne pouvais pas vraiment utiliser avec les stream (autant que je m’en souvienne)

Voici l’idiome que j’aime pour rendre les fonctionnalités identiques à ‘sprintf’, mais en retournant un std :: ssortingng et immunisé contre les problèmes de dépassement de tampon. Ce code fait partie d’un projet open source que j’écris (licence BSD), donc tout le monde est libre de l’utiliser comme vous le souhaitez.

 #include  #include  #include  #include  std::ssortingng format (const char *fmt, ...) { va_list ap; va_start (ap, fmt); std::ssortingng buf = vformat (fmt, ap); va_end (ap); return buf; } std::ssortingng vformat (const char *fmt, va_list ap) { // Allocate a buffer on the stack that's big enough for us almost // all the time. size_t size = 1024; char buf[size]; // Try to vsnprintf into our buffer. va_list apcopy; va_copy (apcopy, ap); int needed = vsnprintf (&buf[0], size, fmt, ap); // NB. On Windows, vsnprintf returns -1 if the ssortingng didn't fit the // buffer. On Linux & OSX, it returns the length it would have needed. if (needed <= size && needed >= 0) { // It fit fine the first time, we're done. return std::ssortingng (&buf[0]); } else { // vsnprintf reported that it wanted to write more characters // than we allotted. So do a malloc of the right size and try again. // This doesn't happen very often if we chose our initial size // well. std::vector  buf; size = needed; buf.resize (size); needed = vsnprintf (&buf[0], size, fmt, apcopy); return std::ssortingng (&buf[0]); } } 

EDIT: quand j’ai écrit ce code, je ne savais pas que cette conformité à la norme C99 était nécessaire et que Windows (ainsi que les versions plus anciennes de glibc) avaient un comportement vsnprintf différent, dans lequel il renvoyait -1 plutôt qu’une mesure définitive de l’espace est nécessaire. Voici mon code révisé, est-ce que tout le monde pourrait y jeter un coup d’œil et si vous pensez que tout va bien, je le modifierai à nouveau pour que le seul coût indiqué soit:

 std::ssortingng Strutil::vformat (const char *fmt, va_list ap) { // Allocate a buffer on the stack that's big enough for us almost // all the time. Be prepared to allocate dynamically if it doesn't fit. size_t size = 1024; char stackbuf[1024]; std::vector dynamicbuf; char *buf = &stackbuf[0]; va_list ap_copy; while (1) { // Try to vsnprintf into our buffer. va_copy(ap_copy, ap); int needed = vsnprintf (buf, size, fmt, ap); va_end(ap_copy); // NB. C99 (which modern Linux and OS X follow) says vsnprintf // failure returns the length it would have needed. But older // glibc and current Windows return -1 for failure, ie, not // telling us how much was needed. if (needed <= (int)size && needed >= 0) { // It fit fine so we're done. return std::ssortingng (buf, (size_t) needed); } // vsnprintf reported that it wanted to write more characters // than we allotted. So try again using a dynamic buffer. This // doesn't happen very often if we chose our initial size well. size = (needed > 0) ? (needed+1) : (size*2); dynamicbuf.resize (size); buf = &dynamicbuf[0]; } } 

Vous pouvez utiliser std :: ssortingng et iostreams avec le formatage, tel que l’appel setw () et d’autres dans iomanip.

Ce qui suit pourrait être une solution alternative:

 void A::printto(ostream outputstream) { char buffer[100]; ssortingng s = "stuff"; sprintf(buffer, "some %s", s); outputstream << buffer << endl; b.printto(outputstream); } 

( B::printto similar), et définir

 void A::print(FILE *f) { printto(ofstream(f)); } ssortingng A::to_str() { ossortingngstream os; printto(os); return os.str(); } 

Bien sûr, vous devriez vraiment utiliser snprintf au lieu de sprintf pour éviter les débordements de tampon. Vous pouvez également modifier sélectivement les sprintfs les plus risqués en << format, pour être plus sûr et changer le moins possible.

Vous devriez essayer le fichier d’en-tête SafeFormat de la bibliothèque Loki ( http://loki-lib.sourceforge.net/index.php?n=Idioms.Printf ). Il est similaire à la bibliothèque de formats de chaînes de boost, mais conserve la syntaxe des fonctions printf (…).

J’espère que ça aide!

Est-ce à propos de la sérialisation? Ou imprimer proprement? Si le premier, envisagez également de booster :: serialization. Il s’agit de sérialisation “récursive” d’objects et de sous-objects.