Domanda

Sto lavorando con un open-source di UNIX strumento che è implementato in C++, e ho bisogno di cambiare un po ' di codice per fare quello che voglio.Vorrei rendere il più piccolo possibile cambiamento, nella speranza di ottenere la mia patch accettato a monte.Soluzioni da implementare in C++ standard e non creare più di dipendenze esterne sono da preferire.

Ecco il mio problema.Ho una classe C++ -- chiamiamolo "A" -- che attualmente utilizza la fprintf() per stampare i suoi pesantemente le strutture di dati formattati da un puntatore al file.Nella sua funzione di stampa, anche in modo ricorsivo chiama definite in maniera identica le funzioni di stampa di diverse categorie di soci ("B" è un esempio).C'è un'altra classe C che è un membro std::string "pippo" che deve essere impostato per la stampa() i risultati di un'istanza di A.Pensate a come un to_str() funzione membro per l'A.

In pseudocodice:

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

Vorrei ricordare che C è abbastanza stabile, ma A e B (e il resto dei dipendenti) sono in uno stato di flusso, in modo che il meno modifiche al codice necessario il meglio.La stampa(FILE* F) di interfaccia, che deve essere preservato.Ho preso in considerazione diversi approcci all'implementazione di Un::to_str(), ciascuna con vantaggi e svantaggi:

  1. Modificare le chiamate a fprintf() a sprintf()

    • Io non avrei dovuto riscrivere qualsiasi stringhe di formato
    • print() potrebbe essere reintrodotto come:fprint(f, questo.to_str());
    • Ma avrei bisogno di assegnare manualmente char[]s, unire un sacco di stringhe c , e, infine, convertire l'array di caratteri a un std::string
  2. Provare a prendere i risultati di una.print() in una stringa di flusso

    • Vorrei convertire tutte le stringhe di formato per << il formato di output.Ci sono centinaia di fprintf()s per la conversione :-{
    • print() dovrebbe essere riscritto, perché non esiste un modo standard che io sappia per creare un flusso di output da un file UNIX gestire (anche se questo ragazzo dice che potrebbe essere possibile).
  3. Utilizzare la Spinta della stringa formato biblioteca

    • Più di dipendenze esterne.Che schifo.
    • Formato sintassi è abbastanza diverso dal printf() per essere fastidioso:

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

  4. L'uso di Qt QString::asprintf()

    • Una diversa dipendenza dall'esterno.

Così, ho esaurito tutte le opzioni possibili?Se è così, quale pensi che sia il mio migliore?Se non che ho trascurato?

Grazie.

È stato utile?

Soluzione

Sto usando #3:la spinta stringa di formato biblioteca - ma devo ammettere che non ho mai avuto alcun problema con le differenze nelle specifiche di formato.

Funziona come un fascino per me - e le dipendenze esterne, potrebbe essere peggio (molto stabile e biblioteca)

A cura:l'aggiunta di un esempio di come usare boost::invece di formato di printf:

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

sarebbe qualcosa di simile a questo con il boost::formato biblioteca:

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

Spero che questo aiuta a chiarire l'utilizzo di boost::formato

Ho usato il boost::formato sprintf / printf sostituzione in 4 o 5 applicazioni (scrivere le stringhe di formato di file, o di output personalizzato di file di log) e non ha mai avuto problemi con differenze di formato.Ci possono essere alcuni (più o meno oscuro) specificatori di formato che sono in modo diverso - ma non ho mai avuto un problema.

In contrasto ho avuto alcune specifiche di formato non riuscivo davvero a che fare con flussi (per quanto mi ricordo)

Altri suggerimenti

Ecco l'idioma mi piace per la realizzazione di funzionalità identiche a 'sprintf', ma la restituzione di un std::string, e immune da problemi di overflow del buffer.Questo codice fa parte di un progetto open source che sto scrivendo (licenza BSD), in modo che tutti si sentano liberi di usare questo come si desidera.

#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:quando ho scritto questo codice, non avevo idea che questo ha richiesto C99 di conformità, e che Windows (così come il precedente glibc) aveva diversi vsnprintf comportamento, in cui si ritorna -1 in caso di errore, piuttosto che una misura definitiva di quanto spazio è necessario.Ecco il mio nuovo codice, potrebbe a tutti di guardare oltre e se si pensa che è ok, vorrei modificare di nuovo per far si che il solo costo elencati:

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

Si può usare std::string e iostreams con la formattazione, come il setw() la chiamata e gli altri in iomanip

Il seguito potrebbe essere una soluzione alternativa:

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

(B::printto simili), e definire

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

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

Naturalmente, si dovrebbe davvero utilizzare snprintf invece di sprintf per evitare l'overflow del buffer.Si potrebbe anche modificare in modo selettivo e più rischioso per sprintfs << formato, per essere più sicuro e ancora cambiare il meno possibile.

Si dovrebbe provare il Loki della biblioteca SafeFormat file di intestazione (http://loki-lib.sourceforge.net/index.php?n=Idioms.Printf).E ' simile ad aumentare la stringa di formato biblioteca, ma mantiene la sintassi del printf(...) funzioni.

Spero che questo aiuta!

Si tratta di serializzazione?O la stampa di un corretto?Nel primo caso, in considerazione boost::serializzazione così.È tutto "ricorsivo" serializzazione di oggetti e sub-oggetto.

Il {fmt} libreria fornisce fmt::sprintf funzione che esegue printfcompatibile con la formattazione (tra cui argomenti posizionali in base alle Specifiche POSIX) e restituisce il risultato come std::string:

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

Disclaimer:Io sono l'autore di questa raccolta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top