XML Streams Module User’s Guide : Chapter 3 A Simple XML Streams Use Case : Examining the Code
Examining the Code
The code for this example is presented in three parts:
declaring the basic classes
making the basic classes serializable
persisting the serializable objects
Declaring the Basic Classes
The code below shows the base class real_property and its two derived classes residential and rural_land. It is based on the example code in the \examples\xmlstreams\real_estate directory.
Here is the declaration for the base class real_property:
 
class real_property
{
public:
real_property() {} // 1
real_property(const RWCString& address,
const RWCString& size)
: address_(address), size_(size)
{}
virtual ~real_property() {} // 2
 
 
bool operator== (const real_property& prop) const {
return address_ == prop.address_;
}
 
private:
RWCString address_;
RWCString size_;
};
//1 This is the explicit default constructor required in serializable classes.
//2 At least one virtual function is needed, because the by-reference streaming facility relies on RTTI, and that requires a v-table.
Here are the declarations for the two derived classes residential and rural_land:
 
class residential : public real_property
{
public:
residential() // 1
{}
residential(const RWCString& address, const RWCString& size,
long footage)
: real_property(address, size),
footage_(footage)
{}
bool operator== (const residential& prop) const {
return real_property::operator==(prop);
}
 
private:
long footage_; //Square footage of the house
};
 
 
class rural_land : public real_property
{
public:
 
rural_land()
{}
 
rural_land(const RWCString& address, const RWCString& size,
double irrigation = 0.0)
: real_property(address, size), irrigation_(irrigation)
{}
 
bool operator== (const rural_land& prop) const {
return real_property::operator==(prop);
}
 
private:
double irrigation_; //Irrigation acres for the property
};
//1 The derived classes also require an explicit default constructor.
Note that the derived classes residential and rural_land inherit a virtual function from the base class and therefore fulfill the requirement for serializable classes.
Making the Basic Classes Serializable
The real estate application references the residential and rural_land objects as pointers to the real_property base class. As stated in “Serialization Requirements” above, to make these classes serializable as pointers you must:
Declare the streamContents() function and the global operators for streaming pointers by putting macros in the header file for each class.
Define the streamContents() function and the global operators for streaming pointers by putting macros in the source file for each class.
Header File Changes
Starting with the header files, add the following macros:
RW_DECLARE_VIRTUAL_STREAM_FNS(classname), which declares the streamContents() member function needed to serialize a class out to a virtual stream. Place this within the class declaration.
RW_DECLARE_STREAMABLE_POINTER(classname), which declares the global input and output operators needed for an object pointer to be written out and later restored. Because this macro declares global operators, you must place it outside the class declaration. The safest location for this macro declaration is immediately following the declaration of the class to which it applies.
RW_DECLARE_STREAMABLE_AS_SELF(classname), which declares the factory for your class and declares its registrar. This allows objects to be created on the fly from the stream.
RW_DECLARE_STREAMABLE_AS_BASE(classname), which declares the factory for your class and declares its registrar for serialization through base class pointers.
Here is the revised code for the header files:
 
// from real_property.h
 
class real_property
{
RW_DECLARE_VIRTUAL_STREAM_FNS(real_property)
 
public:
 
real_property() {}
 
real_property(const RWCString& address,
const RWCString& size)
: address_(address), size_(size)
{}
 
virtual ~real_property() {}
 
bool operator== (const real_property& prop) const {
return address_ == prop.address_;
}
 
private:
RWCString address_;
RWCString size_;
};
 
RW_DECLARE_STREAMABLE_POINTER(real_property)
RW_DECLARE_STREAMABLE_AS_SELF(real_property)
 
// from residential.h
 
class residential : public real_property
{
RW_DECLARE_VIRTUAL_STREAM_FNS(residential)
 
public:
 
residential()
{}
residential(const RWCString& address, const RWCString& size,
long footage)
: real_property(address, size),
footage_(footage)
{}
bool operator== (const residential& prop) const {
return real_property::operator==(prop);
}
 
private:
long footage_; // Square footage of the house
};
 
RW_DECLARE_STREAMABLE_AS_SELF(residential)
RW_DECLARE_STREAMABLE_AS_BASE(residential, real_property)
RW_DECLARE_STREAMABLE_POINTER(residential)
 
 
// from rural_land.h
 
class rural_land : public real_property
{
RW_DECLARE_VIRTUAL_STREAM_FNS(rural_land)
 
public:
 
rural_land()
{}
rural_land(const RWCString& address, const RWCString& size,
double irrigation = 0.0)
: real_property(address, size), irrigation_(irrigation)
{}
bool operator== (const rural_land& prop) const {
return real_property::operator==(prop);
}
 
private:
double irrigation_; // Irrigation acres for the property
};
 
RW_DECLARE_STREAMABLE_AS_SELF(rural_land)
RW_DECLARE_STREAMABLE_AS_BASE(rural_land, real_property)
RW_DECLARE_STREAMABLE_POINTER(rural_land)
Source File Changes
Moving on to the source files, you must now:
Define the streamContents() function using the following macro set:
RW_BEGIN_STREAM_CONTENTS(className)
RW_STREAM_PARENT(parentClass)
RW_STREAM_ATTR_MEMBER(variableLabel,variableName)
RW_END_STREAM_CONTENTS()
The definitions generated through these macros guarantee symmetry between the serialization output and input operations.
Define the global insertion and extraction operators that support the serialization of pointers to objects, using the following macro set:
RW_DEFINE_STREAMABLE_POINTER(className)
Apply this macro to all classes involved in the serialization.
RW_DEFINE_STREAMABLE_AS_SELF(className)
Apply this macro to a class if its instantiations will be serialized as pointers directly to the instantiations.
RW_DEFINE_STREAMABLE_AS_BASE(derivedClass,baseClass)
Apply this macro to a class derived from a base class if its instantiations will be serialized as pointers to the base class.
If you want to allow for the possibility that these objects might be serialized as pointers directly to themselves, you need to apply RW_DEFINE_STREAMABLE_AS_SELF as well. In the code that follows, both RW_DEFINE_STREAMABLE_AS_BASE and RW_DEFINE_STREAMABLE_AS_SELF are applied.
Here is the source code for your three classes, modified as needed with the macros described above:
 
// from real_property.cpp
 
RW_BEGIN_STREAM_CONTENTS(real_property) //1
{
RW_STREAM_ATTR_MEMBER(address, address_) //2
RW_STREAM_ATTR_MEMBER(size, size_)
}
RW_END_STREAM_CONTENTS //3
//1 Begins the streamContents() function definition for the class real_property.
//2 Generates the code for the streaming of the address_ variable. The first argument can be anything, but clearly something close to the variable name is preferable. The second argument must be the exact variable name.
//3 Ends the streamContents() function definition.
 
// from residential.cpp
 
RW_BEGIN_STREAM_CONTENTS(residential)
{
RW_STREAM_PARENT(real_property) //1
RW_STREAM_ATTR_MEMBER(footage, footage_) //2
}
RW_END_STREAM_CONTENTS
 
//1 Generates the code for the streaming of variables inherited from the parent class. This macro must appear immediately after the RW_BEGIN_STREAM_CONTENTS macro.
//2 Generates the code for the streaming of the footage_ variable, which is an extension to the variables in the base class.
Note that the code below parallels the code for residential.
 
// from rural_land.cpp
RW_BEGIN_STREAM_CONTENTS(rural_land)
{
RW_STREAM_PARENT(real_property)
RW_STREAM_ATTR_MEMBER(irrigation, irrigation_)
}
RW_END_STREAM_CONTENTS
Persisting the Serializable Objects
Now that the classes have been prepared with serialization support, consider the code that could be used to persist the residential and rural_land objects — as pointers to real_property — at application shutdown, and restore these objects when the application is restarted. The main logic for the example application is in listing.cpp.
But before looking at the main logic, there is one more setup requirement. Recall from the use case description that at application shutdown the RWTPtrOrderedVector collection of real_property objects is written out to a file as a single operation. When the application is restarted, it reads in the file and restores the RWTPtrOrderedVector collection with all its real_property objects.
For this to work, you must make the RWTPtrOrderedVector collection serializable. To do this, apply the macro RW_DECLARE_STREAMABLE_PTR_SEQUENCE() on the collection. This macro declares the global input and output operators needed for streaming a collection of pointers. This macro can occur anywhere in the code so long as it is guaranteed to occur after the #include of the header file for RWTPtrOrderedVector, which is tpordvec.h. Since this include occurs in listing.cpp, this is the safest place to apply the macro.
Here is the code that includes the collection and prepares it for serialization:
 
// from listing.cpp
 
// Get the header file for RWTPtrOrderedVector
#include <rw/tpordvec.h>
 
// Create global operators to support streaming of an
// RWTPtrOrderedVector collection
RW_DECLARE_STREAMABLE_PTR_SEQUENCE(RWTPtrOrderedVector)
Now you are ready to look at the main logic, beginning with the code for persisting the collection of real_property objects at application shutdown.
 
// Here is the shutdown processing, which
// - creates an output stream to a file
// - serializes out the RWTPtrOrderedVector collection,
// whose name is “properties”
 
ofstream fout(“properties.xml”); //1
 
RWObjectOutputStream out = //2
RWXmlObjectOutputStreamImp::make(fout,
RWXmlObjectOutputStreamImp::sequenceTag); //3
out << properties; //4
//1 Create an output file stream to the file properties.xml.
//2 Create an XML object output stream, passing the output file stream as a parameter.
//3 As the second parameter, specify sequenceTag, denoting that the object to be serialized out is a collection.
//4 Stream out the RWTPtrOrderedVector collection properties.
Next, look at the code for restoring the collection at application startup.
 
// Here is the startup processing, closely mirrors
// the shutdown processing.
 
ifstream fin(“properties.xml”); //1
 
RWObjectInputStream in = //2
RWXmlObjectInputStreamImp::make(fin,
in >> properties; //3
//1 Create an input file stream to read in the file properties.xml.
//2 Create an XML object input stream, passing the input file stream as a parameter.
//3 Stream the data into the properties collection.