C ++ fstream - creating custom formatting flags

I need to create new flags for the output file format. I have a class

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

and I want to use it like:

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

this will save the xml file.

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

and this is a json file.

How can i do this?

+6
source share
3 answers

You can easily create your own manipulators, either by capturing an existing flag or using std::ios_base::xalloc to get a new memory stream, for example. (in the Foo implementation file:

 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 and json in your header and the job is done.

(Having said that, I rather think that manipulators are a bit abusive. Formats such as xml go beyond simple, local formatting and it is best to handle the separate class that owns ostream and writes the entire stream, not just individual objects.)

+6
source

This problem is the biggest mistake in the iostream library.

James Kanze's solution is partial, which will work in your own classes, but overall, objects are provided with a great way to stream.

My usual way is to create your own wrapper class with a function that you can pass into your stream, and with xml it will contain overloads up to xml_node() or xml_attribute() for example.

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

sets the attribute identifier to a value that is in the variable in xml format.

I also wrote node areas so that they record the stream of node-opening text by construction and automatically record the closing logic upon destruction.

The advantage of my method over James Kanze's solution is that it is extensible. I think the comment by James Kanze suggests that he does not support his decision and is likely to use something more than mine.

With the above solution, to add more formats, you need to edit the <<operator is ubiquitous, while the json formatting code will be a completely different set of functions, and if you added another format, you would add code for it without having to edit any existing code.

For input, by the way, for XML, you would use the existing DOM or SAX parser and not use iostream in this way.

+1
source

The easiest way that comes to mind is to start by creating a tag type and one instance of it:

 struct JsonStreamTag {} json; 

Then let this tag build an object to wrap the stream:

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

The protected constructor so you can use some_ostream << json (1) to build a JsonStream . Another insert statement (2) performs the actual formatting. Then you define write_json() (3) overload for each corresponding type:

 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. 

Alternatively, omit (2) and add overloads for operator<<(JsonStream&, T) .

Then just follow the same process to write the appropriate XmlStream using XmlStreamTag and write_xml() . This suggests that your output can be completely constructed from the specific values ​​you write; if you need some kind of header or footer, the same thing in every file you write, just use the constructor and destructor:

 XmlStream(std::ostream& ostream) : ostream(ostream) { ostream << "<?xml version=\"1.0\"?><my_document>" } ~XmlStream() { ostream << "</my_document>"; } 
0
source

Source: https://habr.com/ru/post/910320/


All Articles