2014-11-06 12 views
10

Supponiamo che io abbia una funzione che prende un parametro o e scrive in tale ostream. Un'implementazione operator << sarebbe un buon esempio.Devo creare un ostream temporaneo usando streambuf di un altro?

ostream& operator << (ostream& o, const MyThing& t) 
{ 
    // ... interesting code here ... 
    return o; 
} 

All'interno della funzione, potrei voler specificare le opzioni di formattazione sul flusso. Ad esempio, potrei desiderare che un numero venga stampato come esadecimale, indipendentemente da come sia configurato lo o quando viene passato alla funzione.

In secondo luogo, potrei voler fare ipotesi sulle attuali flag di formattazione. Ad esempio, sarebbe bello poter supporre che i numeri siano stati formattati come decimali a meno che non chieda il contrario.

Infine, quando termina la funzione, desidero che le opzioni di formattazione su o siano le stesse di prima della chiamata alla funzione, in modo tale da apparire invariate al chiamante. Questa è semplicemente una questione di cortesia per il chiamante.

Fino ad ora ho raggiunto questo attraverso la creazione di un locale ostringstream all'interno della funzione, facendo tutto il mio lavoro su quella (compresa l'impostazione delle opzioni di formattazione), e l'invio del .str()-o al termine della funzione. La domanda StackOverflow here suggerisce che le persone più intelligenti di me adottano lo stesso approccio. Tuttavia, mi dà fastidio il fatto che tengo così tanti dati in strings che potrebbero forse essere inviati all'output in precedenza (le stringhe possono diventare piuttosto grandi).

Ho due domande:

1) E 'legale, idiomatica, buona forma, ecc di creare un (stack based) ostream temporaneo intorno o.rdbuf() e fare il mio lavoro su quel ostream? I miei test e la pagina allo cppreference.com sembrano suggerire che posso.

ostream& operator << (ostream& o_, const MyThing& t) 
{ 
    ostream o (o_.rdbuf()); 
    // write stuff to "o", 
    // setting formatting options as I go. 
    return o_; // Formatting on the parameter ostream o_ unchanged. 
} 

2) Esiste un altro modo migliore che non ho considerato?

risposta

0

Le impostazioni possono essere memorizzate in un tipo di oggetto chiamato oggetto fmtflags, definito in una classe denominata ios, che è inclusa in iostream. È possibile dichiarare uno di questi oggetti, ma è necessario dichiararlo utilizzando l'operatore di risoluzione dell'ambito.

la seguente dichiarazione salverà alcuni aspetti dello stato formato nelle old_settings variabili:

ios::fmtflags old_settings = cout.flags(); 

Poi, dopo aver fatto l'output utilizzando la nuova impostazione, è possibile ripristinare la vecchia impostazione chiamando la stessa funzione con le vecchie impostazioni come argomento:

cout.flags(old_settings); 

altre impostazioni possono essere ottenuti e ripristinati con funzioni membro. Ad esempio,

int old_precision = cout.precision(); 

salverà la specifica di precisione corrente.Poi

cout.precision(old_precision); 

ripristinerà la precisione al valore originale

+0

Quelle chiamate sarebbe meglio metterlo nel costruttore e distruttore di qualche oggetto basato su stack. Immagino sia quello che fanno le classi Boost. – peterpi

1

Questa non è una cattiva soluzione; è sicuramente legale. Non penso che lo sia troppo comune, quindi è probabilmente una buona idea commentare come perché lo stai facendo.

La soluzione più frequente che ho visto qui è quello di creare una classe state saver , che farà risparmiare tutto lo stato è necessario (tipicamente, flags(), precision() e fill()) nel costruttore , e ripristinarlo in il distruttore, quindi su imposta tutte le opzioni desiderate. (Potrebbe essere possibile utilizzare copyfmt per questo, anche se questa copia anche cose come la maschera eccezione, che probabilmente non si vuole giocare con.)