2012-03-09 10 views
6

Ho bisogno di creare nuovi flag per il formato del file di output. Ho una classeC++ fstream - creazione di propri flag di formattazione

class foo{ 
    bar* members; 
    ofstream& operator<<(ofstream&); 
    ifstream& operator>>(ifstream&); 
}; 

e voglio usarlo come:

fstream os('filename.xml'); 
foo f; 
os << xml << f; 
os.close(); 

questo vi farà risparmiare un file dixml.

fstream os('filename.json'); 
foo f; 
os << json << f; 
os.close(); 

e questo un file diJSON.

Come posso fare questo?

risposta

6

È possibile creare facilmente yor proprio manipolatori, o dirottare un bandiera esistente o utilizzando std::ios_base::xalloc a ottenere una nuova memoria specifica dello stream , ad es (Nel file di implementazione di Foo:

static int const manipFlagId = std::ios_base::xalloc(); 

enum 
{ 
    fmt_xml,  // Becomes the default. 
    fmt_json 
}; 

std::ostream& 
xml(std::ostream& stream) 
{ 
    stream.iword(manipFlagId) = fmt_xml; 
    return stream; 
} 

std::ostream& 
json(std::ostream& stream) 
{ 
    stream.iword(manipFlagId) = fmt_json; 
    return stream; 
} 

std::ostream& 
operator<<(std::ostream& dest, Foo const& obj) 
{ 
    switch (dest.iword(manipFlagId)) { 
    case fmt_xml: 
     // ... 
     break; 
    case fmt_json: 
     // ... 
     break; 
    default: 
     assert(0); // Or log error, or abort, or... 
    } 
    return dest; 
} 

Declare xml e json nell'intestazione, e il lavoro è fatto

(Detto questo, io invece penso che questo è un po 'di un abuso di. manipolatori. formati come XML vanno oltre la semplice, la formattazione locale, e sono meglio gestiti da una classe separata, che possiede la ostream, e scrive l'intero flusso, e non solo oggetti singoli.)

0

Il modo più semplice che viene in mente è quello di iniziare con la creazione di un tipo di tag e di una singola istanza di esso:

struct JsonStreamTag {} json; 

Allora un tale tag costruire un oggetto di avvolgere il flusso:

class JsonStream { 
public: 

    // (1) 
    friend JsonStream operator<<(std::ostream& ostream, const JsonStreamTag&) { 
     return JsonStream(ostream); 
    } 

    // (2) 
    template<class T> 
    friend JsonStream& operator<<(JsonStream& json_stream, const T& value) { 
     write_json(json_stream.ostream, value); // (3) 
     return json_stream; 
    } 

protected: 

    JsonStream(std::ostream& ostream) : ostream(ostream) {} 

private: 

    std::ostream& ostream; 

}; 

Il costruttore è protected per garantire che è possibile utilizzare solo some_ostream << json (1) per creare un JsonStream. L'altro operatore di inserimento (2) esegue la formattazione effettiva. È quindi definire un sovraccarico di write_json() (3) per ogni tipo rilevanti:

void write_json(std::ostream& stream, int value) { 
    stream << value; 
} 

void write_json(std::ostream& stream, std::string value) { 
    stream << '"' << escape_json(value) << '"'; 
} 

// Overloads for double, std::vector, std::map, &c. 

In alternativa, omettere (2) e aggiungere sovraccarichi per operator<<(JsonStream&, T) invece.

Quindi basta seguire la stessa procedura per scrivere il corrispondente XmlStream utilizzando XmlStreamTag e write_xml(). Questo presume che il tuo output possa essere costruito completamente dai valori particolari che stai scrivendo; se avete bisogno di un po 'di intestazione o piè di pagina che è la stessa attraverso ogni file potrai scrivere, basta utilizzare il costruttore e distruttore:

XmlStream(std::ostream& ostream) : ostream(ostream) { 
    ostream << "<?xml version=\"1.0\"?><my_document>" 
} 

~XmlStream() { 
    ostream << "</my_document>"; 
} 
+1

Non penso che questa sia una buona idea. Non funziona per cose come 'out << json <<" Intestazione: "<< obj;' per esempio. (Naturalmente, per qualcosa come XML, questo non avrebbe comunque senso. Ma questo è un argomento per non usare i manipolatori in primo luogo.) –

+0

@JamesKanze: La domanda non è chiaramente definita. Allo scopo di rispondere letteralmente alla domanda, ho ipotizzato che uno "stream JSON" avrebbe trattato tutto come un valore JSON. Ma penso che una cosa del genere sia sbagliata in primo luogo. –

+0

XML (e JSON, per quanto ne so) non sono flussi; sono strutture più complesse. Un tipo non correlato non deve generare XML o JSON su un 'ostream'; dovrebbe inserirlo in qualche istanza di una struttura dati XML o JSON, che quindi si occuperebbe dell'output in streaming, a livello di file. –

1

Questo problema è il più grande difetto nella libreria iostream.

La soluzione di James Kanze è una soluzione parziale che funzionerà nelle tue classi, ma in generale agli oggetti viene assegnato un modo distinto di streaming.

Il mio mezzo abituale è quello di creare la mia classe wrapper con una funzione che è possibile passare nel flusso e con xml conterrà sovraccarichi a xml_node() o xml_attribute() ad es.

os << xml_attribute("Id", id); 

imposterà l'attributo Id a qualsiasi cosa si trovi nella variabile in formato xml.

Ho anche scritto gli ambiti del nodo in modo che scriveranno per eseguire lo streaming del testo di apertura del nodo durante la costruzione e scrivere automaticamente la logica di chiusura in caso di distruzione.

Il vantaggio del mio metodo rispetto alla soluzione di James Kanze è che è estensibile. Penso che il commento conclusivo di James Kanze suggerisca che non approva la sua soluzione e probabilmente userebbe qualcosa di più simile al mio.

Con la soluzione di cui sopra, al fine di aggiungere ulteriori formati si deve modificare l'operatore < < funzioni in tutto il luogo, mentre il codice di formattazione JSON sarebbe una serie completamente diversa di funzioni e se si è aggiunto ancora un altro formato che si aggiungi il codice per questo senza dover modificare alcun codice esistente.

Per l'introduzione, per l'XML, si utilizzerà un parser DOM o SAX esistente e non si userebbe direttamente iostream in questo modo.

+0

Non penso che l'utilizzo di uno stream direttamente per l'output di XML sia una soluzione appropriata. Un flusso è un'astrazione di un flusso, non una struttura gerarchica dei dati. Per l'output di XML, utilizzerei una libreria come Xerces; qualcosa che consente l'inserimento in una struttura gerarchica. Per il resto: le classi wrapper sono utili se hai bisogno di una formattazione insolita per un tipo built-in o per strutture molto complicate, ma i manipolatori funzionano bene in un gran numero di casi. –

+0

Per essere più precisi (e forse questo è ciò che intendi anche tu): l'output in un formato globalmente diverso dovrebbe essere fatto con un meccanismo diverso dallo stream: i manipolatori personalizzati sono per singoli tipi personalizzati, non qualcosa che influenza l'output di tutti i tipi . (E non puoi aggiungere opzioni di formattazione a 'int'). –

Problemi correlati