Frage

Ich arbeite mit einem Open-Source-UNIX-Tool, das in C ++ implementiert ist, und ich brauche einige Codes zu ändern, um es zu tun, was ich will. Ich möchte eine möglichst kleine Änderung in der Hoffnung vor immer mein Patch akzeptiert machen. Lösungen, die in Standard-C ++ implementierbar sind und nicht mehr externe Abhängigkeiten schaffen, werden bevorzugt.

Hier ist mein Problem. Ich habe eine C ++ Klasse - nennen wir es „A“ -, die derzeit verwendet fprintf () seine stark formatierten Datenstrukturen auf einen Dateizeiger zu drucken. In seiner Druckfunktion, es ruft rekursiv auch die gleich definierten Druckfunktionen von mehreren Mitgliedsklassen ( „B“ ist ein Beispiel). Denken Sie daran, es gibt eine andere Klasse C, die ein Mitglied std :: string hat „foo“, die auf die print () Ergebnisse einer Instanz von A eingestellt werden muss, als to_str () Member-Funktion für A.

In Pseudo-Code:

class A {
public:
  ...

  void print(FILE* f);
  B b;

  ...  
};

...

void A::print(FILE *f)
{
  std::string s = "stuff";
  fprintf(f, "some %s", s);
  b.print(f);
}

class C {
  ...
  std::string 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());

soll ich erwähnen, dass C ziemlich stabil ist, aber A und B (und der Rest von A berechtigten Personen) ist in einem Zustand des Flusses, so dass der weniger Code ändert, desto besser erforderlich. Der aktuelle Druck (FILE * F) Schnittstelle muss auch erhalten bleiben. Ich habe verschiedene Ansätze in Betracht gezogen A :: to_str Umsetzung (), die jeweils mit Vor- und Nachteile:

  1. Ändern Sie die Anrufe zu fprintf () zu sprintf ()

    • Ich würde keine Formatstrings neu zu schreiben
    • print () könnte neu implementiert als: fprint (f, this.to_str ());
    • Aber ich müsste manuell char [] s zuweisen, eine Menge c Strings zusammenführen, und schließlich den Zeichen-Array zu einem std :: string
    • konvertieren
  2. Versuchen Sie, die Ergebnisse von A.Print () in einem String-Stream zu fangen

    • Ich würde all die Formatstrings zu << Ausgabeformat konvertieren. Es gibt Hunderte von fprintf () s zu konvertieren: - {
    • print () müssten neu geschrieben werden, weil es keine Standardmethode ist, die ich kenne einen Ausgangsstrom von einem UNIX-Datei-Handle (obwohl dieser Mann sagt, dass es möglich sein kann).
  3. Mit Boost-String Formatbibliothek

    • Weitere externe Abhängigkeiten. Yuck.
    • Format Syntax ist unterschiedlich genug von printf () als störend:

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

  4. Die Nutzung von Qt QString :: asprintf ()

    • Eine andere externe Abhängigkeit.

So habe ich alle möglichen Optionen erschöpft? Wenn ja, welche denken Sie, ist meine beste Wette? Wenn nicht, was habe ich übersehen?

Danke.

War es hilfreich?

Lösung

Ich bin mit # 3: die String-Format Bibliothek Boost - aber ich muss zugeben, dass ich noch nie ein Problem mit den Unterschieden in Formatspezifikationen hatte

.

Funktioniert wie ein Zauber für mich - und die externen Abhängigkeiten könnten schlimmer sein (eine sehr stabile Bibliothek)

Editiert: Hinzufügen eines Beispiels, wie Boost verwenden :: Format statt printf:

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

würde mit der boost :: Formatbibliothek so etwas wie diese:

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

Hope, das hilft die Verwendung von boost :: Format zu klären

Ich habe in 4 oder 5 Anwendungen (Schreiben formatierten Strings Dateien oder benutzerdefinierte Ausgabe an Logfiles) und hatte nie Probleme mit dem Format Unterschieden boost :: Format als sprintf / printf Ersatz verwendet. Möglicherweise gibt es einige (mehr oder weniger dunkel) sein Formatbezeichner, die anders sind -. Aber ich hatte nie ein Problem

Im Gegensatz hatte ich einige Formatspezifikationen konnte ich nicht wirklich mit Strömen tun (so viel wie ich mich erinnere)

Andere Tipps

Hier ist das Idiom ich identisch mit sprintf zur Herstellung von Funktionalität wie, aber ein std :: string Rückkehr und Immun-Überlaufprobleme zu puffern. Dieser Code ist Teil eines Open-Source-Projekt, das ich (BSD-Lizenz) Ich schreibe, so das jeder frei fühlen zu verwenden, wie Sie wollen.

#include <string>
#include <cstdarg>
#include <vector>
#include <string>

std::string
format (const char *fmt, ...)
{
    va_list ap;
    va_start (ap, fmt);
    std::string buf = vformat (fmt, ap);
    va_end (ap);
    return buf;
}



std::string
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 string 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::string (&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 <char> buf;
        size = needed;
        buf.resize (size);
        needed = vsnprintf (&buf[0], size, fmt, apcopy);
        return std::string (&buf[0]);
    }
}

EDIT: Wenn ich diesen Code schrieb, hatte ich keine Ahnung, dass diese C99-Konformität erforderlich und dass Windows (sowie ältere glibc) hatte unterschiedliches vsnprintf Verhalten, in dem es gibt -1 für das Scheitern, sondern als eine endgültige Maßnahme von wie viel Speicherplatz benötigt wird. Hier ist mein überarbeiteter Code ist, könnte jeder schauen Sie vorbei und wenn Sie denken, es ist in Ordnung, werde ich erneut bearbeiten zu machen, dass die einzig Kosten aufgeführt:

std::string
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<char> 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, i.e., not
        // telling us how much was needed.

        if (needed <= (int)size && needed >= 0) {
            // It fit fine so we're done.
            return std::string (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];
    }
}

Sie können mit std :: string und iostreams mit Formatierungen wie der setw () -Aufruf und andere in iomanip

Im Folgenden könnte eine alternative Lösung sein:

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

(B::printto ähnlich) und definieren

void A::print(FILE *f) {
    printto(ofstream(f));
}

string A::to_str() {
    ostringstream os;
    printto(os);
    return os.str();
}

Natürlich sollten Sie wirklich snprintf anstelle von sprintf Pufferüberlauf zu vermeiden. Sie könnten auch selektiv die riskante sprintfs ändern << Format, als sicherer und noch so wenig wie möglich ändern.

Sie sollten versuchen, die SafeFormat Header-Datei der Loki-Bibliothek ( http: / /loki-lib.sourceforge.net/index.php?n=Idioms.Printf ). Es ist ähnlich wie boost Stringformatbibliothek, sondern hält die Syntax der printf (...) Funktionen.

Ich hoffe, das hilft!

Ist das etwa Serialisierung? Oder Bedrucken richtig? Wenn der ehemalige, sollten auch boost :: Serialisierung. Es geht um „rekursiven“ Serialisierung von Objekten und Unterobjekt.

die {} fmt Bibliothek fmt::sprintf Funktion bereitstellt, die nach printf kompatibelen Formatierungs (einschließlich Positions Argumenten ausführen zum POSIX-Spezifikation ) und gibt das Ergebnis als std::string rel="nofollow:

std::string s = fmt::sprintf("The answer is %d.", 42);

Hinweis . Ich bin der Autor dieser Bibliothek

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top