2012-03-09 17 views
6

Necesito crear nuevas banderas para el formato del archivo de salida. Tengo una claseC++ fstream - Creando banderas de formato propias

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

y quiero usarlo como:

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

esto ahorrará un archivo XML .

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

y este archivo una JSON.

¿Cómo puedo hacer esto?

Respuesta

6

Usted puede crear fácilmente yor propios manipuladores, ya sea el secuestro de un indicador existente o usando std::ios_base::xalloc a obtener nueva memoria específica del flujo, por ej. (En el archivo de implementación de 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; 
} 

Declarar xml y json en su cabecera, y el trabajo está hecho

(Una vez dicho esto, yo más bien creo que esto es un poco de un abuso de. manipuladores. formatos como XML van más allá de un formato sencillo, local y se manejan mejor por una clase separada, propietaria de la ostream y escribe toda la corriente, y no sólo objetos individuales.)

0

La forma más simple que viene a la mente es comenzar mediante la creación de un tipo de etiqueta y una única instancia de los mismos:

struct JsonStreamTag {} json; 

continuación, vamos a una etiqueta tal construcción de un objeto a envolver la corriente:

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; 

}; 

El constructor es protected para asegurarse de que solo puede usar some_ostream << json (1) para construir un JsonStream. El otro operador de inserción (2) realiza el formateo real. A continuación se define una sobrecarga de write_json() (3) para cada tipo relevante:

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. 

Alternativamente, omitir (2) y añadir sobrecargas de operator<<(JsonStream&, T) lugar.

Luego solo siga el mismo proceso para escribir el XmlStream correspondiente usando XmlStreamTag y write_xml(). Esto supone que su salida puede construirse completamente a partir de los valores particulares que está escribiendo; si necesitas algo de encabezado o pie de página que es el mismo en todos los archivos que voy a escribir, sólo tiene que utilizar el constructor y el destructor:

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

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

No creo que esta sea una muy buena idea. No funciona para cosas como 'out << json <<" Encabezado: "<< obj;' por ejemplo. (Por supuesto, para algo como XML, esto no tendría sentido de todos modos. Pero ese es un argumento para no usar manipuladores en primer lugar.) –

+0

@JamesKanze: La pregunta no está claramente definida. Para los propósitos de responder la pregunta literalmente, asumí que una "secuencia JSON" trataría todo como un valor JSON. Pero creo que tal cosa está mal dirigida en primer lugar. –

+0

XML (y JSON, hasta donde yo sé) no son transmisiones; son estructuras más complejas. Un tipo no relacionado no debe generar XML o JSON en un 'ostream'; debe insertarlo en alguna instancia de una estructura de datos XML o JSON, que luego se encargaría de la salida transmitida, a nivel de archivo. –

1

Este problema es el mayor defecto en la biblioteca iostream.

La solución de James Kanze es una solución parcial que funcionará en sus propias clases, pero en general los objetos reciben una forma distinta de transmisión.

Mi medio habitual es crear mi propia clase contenedora con una función que pueda pasar a la secuencia y con xml contener sobrecargas a xml_node() o xml_attribute() p.

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

establecerá el atributo Id a lo que está en la variable en formato xml.

También he escrito los ámbitos de los nodos para que escriban para transmitir el texto de apertura de nodos en la construcción y escriban automáticamente la lógica de cierre en la destrucción.

La ventaja de mi método sobre la solución de James Kanze es que es extensible. Creo que el comentario final de James Kanze sugiere que no respalda su solución y que probablemente usaría algo más parecido al mío.

Con la solución anterior, para agregar más formatos, debe editar las funciones del operador < < en todas partes, mientras que el código de formato json sería un conjunto de funciones completamente diferente y si agrega otro formato lo haría agregue el código sin tener que editar ningún código existente.

Para ingresar, por cierto, para XML usaría un analizador DOM o SAX existente y no usaría iostream directamente de esta manera.

+0

No creo que el uso de una transmisión de flujo directamente a salida XML es una solución adecuada. Una secuencia es una abstracción de una secuencia, no una estructura de datos jerárquica. Para generar XML, usaría una biblioteca como Xerces; algo que permite la inserción en una estructura jerárquica. Por lo demás: las clases de contenedor son útiles si necesita un formato inusual para un tipo incorporado, o para estructuras muy complicadas, pero los manipuladores funcionan bien en una gran cantidad de casos. –

+0

Para ser más preciso (y tal vez eso es lo que quiere decir también): generar un formato diferente a nivel mundial debe hacerse con un mecanismo diferente al flujo: los manipuladores personalizados son para tipos únicos personalizados, no algo que afecte a la salida de todos los tipos . (Y no puede agregar opciones de formato a 'int'). –