This is part 14 of my Implementing
IXmlWriter post series.
Today I will add support for writing the generated XML to a C++ stream to last time’s
Finally the reason why I’ve insisted on calling this series
IXmlWriter (instead of
StringXmlWriter) should become clear: I’ve been planning on supporting writing the generated XML to more than just a string. Specifically, today I will add the ability to write the XML to a C++
ostream object, a base class in the C++ iostream library which defines a writable stream.
To this end, I decided to write a pure-virtual interface called
IXmlWriter which contains all the relevant methods for generating XML (e.g.
WriteComment(), etc.), and two concrete classes which implement this interface:
StringXmlWriter (which writes to a string as before) and
OstreamXmlWriter (which writes to a user-provided
ostream instance). I moved the XML-generating logic from
OstreamXmlWriter and implemented
StringXmlWriter in terms of
OstreamXmlWriter and a
The area where I probably spent the most time was deciding how to pass the
ostream instance to
OstreamXmlWriter and how
OstreamXmlWriter would store it, if necessary. I came up with the following options:
OstreamXmlWriter is passed a copy (or const reference) of the
ostream instance in its constructor and stores a copy of the
ostream instance as a member variable. Advantages: The typical idiom for most variables passed in the constructor. Disadvantages: For correctness, this requires a semantically correct copy constructor which is very difficult, and often impossible, to write for streams.
OstreamXmlWriter is passed a reference (pointer) to the
ostream instance in its constructor and stores a reference (pointer) to the
ostream instance as a member variable. Advantages: Relatively simple. Disadvantages:
OstreamXmlWriter becomes implicitly tied to the lifetime of the passed object, which means that a user must be sure that
OstreamXmlWriter is destroyed before the
ostream instance is. Code such as
stringstream* ss = new stringstream(); XmlWriter w(ss); delete ss; w.WriteStartElement("blah"); may result in hard-to-find bugs.
OstreamXmlWriter is passed a smart pointer object (e.g.
boost::shared_ptr) which points to the
ostream instance in its constructor and stores a copy of the smart pointer object as a member variable. Advantages: Lifetime issues are handled correctly. Disadvantages: The
ostream instance must be constructed on the heap and never the stack. We expose a dependency on a smart pointer implementation that users probably shouldn’t care about. (Alternative: Take a heap-constructed
ostream* in the constructor, store as a smart pointer member variable. However, what if a stack-constructed
ostream is passed?)</li
OstreamXmlWriter takes a reference to the
ostream instance as an extra parameter for every XML writing function. Advantages: Parallels
operator<<, no lifetime issues. Disadvantages: Breaks
Based on these observations, I decided to go with (2). Here’s the new test case:
OstreamXmlWriter xmlWriter(ss, StringXmlWriter::Formatting_Indented);
// ss.str() should equal (whitespace is important):
// <child att="value"/>
Because the source code is starting to get unwieldy (chorus: Too late!), I have linked to the source code files rather than insert the source code in-line with the post. Here’s the latest source code:
Unfortunately, the source code for this post has been lost.