6.2 Getting Started
These are the basic steps required to add serialization support to your classes:
1. Identify all classes that need to be serialized. Include all abstract base and concrete classes. (See Section 6.2.1, “Serialization Requirements.”)
2. Declare the streaming operators by putting macros in the header file for each class. (See Section 6.2.2.1, “Declaring the Streaming Operators.”)
3. Define the streamContents() function by putting macros in the source file for each class. (See Section 6.2.2.2, “Defining the streamContents() Function.”)
4. Write your object to an object stream and read it back. (See Section 6.2.2.3, “Streaming the Serialized Object.”)
6.2.1 Serialization Requirements
Before you begin, look at your classes in terms of the requirements for serialization support:
*Identify all classes that need to be serialized.
For a polymorphic class hierarchy to be serializable, it must derive from a class that supports serialization, so include both concrete classes and abstract base classes.
In order to support serialization, classes must be compiled with run-time type identification (RTTI) enabled. If your compiler/system requires a flag or setting in order to support RTTI, make sure this flag is on. If your class is compiled into a separate library, and you want to provide it with serialization support, make sure RTTI is enabled during compilation of that library as well.
Derived classes must have a base class that supports serialization if any inaccessible members of the base class need to be serialized, or if you wish to serialize a derived object as a pointer to base.
Some of these base classes may already support serialization. For a list of such classes in the Essential Tools Module, including most Rogue Wave containers, see “Polymorphic Persistence” in the Essential Tools Module User’s Guide. If you are using these classes, see Section 6.5.2, “Making RWCollectable Objects That Are Also Serializable.”
*Make sure the classes have the structure to support serialization.
Object streaming normally requires a public default constructor. If your class does not have one, see Section 6.4.5, “Default Constructors.”
*Consider whether some classes will require external serialization.
Normally, you should use intrusive serialization for any class that you can change. If its base class requires external serialization, however, you must use external serialization for the derived class as well. For examples, see Section 6.4.6, “External Serialization.”
Classes within a hierarchy must either use external serialization or use the intrusive style. Mixed hierarchies are not supported.
6.2.2 Simple Examples
This section includes a few simple examples on how to use the Serialization package. The code in Section 6.2.2.1 through Section 6.2.2.3 shows how to add basic serialization support to the class real_property. The code is taken from real_property.h, real_property.cpp, and real_estate.cpp in the examples\serial\simple directory. The examples in Section 6.2.2.4 and Section 6.2.2.5 show two ways to stream a serialized object: compact object streams working with data streams from the Streams package, and compact object streams working with virtual streams from the Essential Tools Module.
6.2.2.1 Declaring the Streaming Operators
First, the streaming operators are declared by putting the RW_DECLARE_VIRTUAL_STREAM_FNS macro at the beginning of the class declaration.
 
// real_property.h
 
class real_property
{
RW_DECLARE_VIRTUAL_STREAM_FNS(real_property) // 1
 
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_;
};
//1 This macro declares streaming operators for the real_property class.
6.2.2.2 Defining the streamContents() Function
Next, the streamContents() function is defined for this class by putting macros in the source file for the class.
 
// 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 This macro begins the definition of the streamContents() function.
//2 This macro defines the code for inserting and extracting data to and from the address_ and size_ data members of the real_property class.
//3 The streamContents() function is closed out with this macro.
6.2.2.3 Streaming the Serialized Object
Now an instance of class real_property can be written to a file and read back.
 
// real_estate.cpp
 
real_property real1
("1980 Main St. Corvallis, Oregon","50x100"); // 1
real_property real2;
{ // 2
 
ofstream fout("real_estate.dat"); // 3
 
RWpostream postr(fout);
RWObjectOutputStream out = RWCompactObjectOutputStreamImp::
make(RWDataToVirtualOutputStreamImp::
make(postr)); // 4
out << real1; // 5
}
 
ifstream fin("real_estate.dat"); // 6
RWpistream pistr(fin);
RWObjectInputStream in = RWCompactObjectInputStreamImp::
make(RWDataFromVirtualInputStreamImp::
make(pistr)); // 7
in >> real2; // 8
//1 Create a real_property object.
//2 Create a local scope so the output file closes automatically when it’s no longer needed.
//3 Open a file for output.
//4 Create a data stream using the standard file stream just opened and then create a compact object output stream from the data stream.
//5 Stream out the contents of real1 to the file in compact format.
//6 Now open the file for input.
//7 Create a data stream and then create a compact object input stream from the data stream.
//8 Read the data from the first variable (real1) into the second (real2).
A more complete version of this example with more explanation is presented in Section 6.4.1, “Saving and Restoring an Object by Value.” If you wish to stream objects as pointers, see Section 6.4.2, “Saving and Restoring an Object by Pointer.”
6.2.2.4 Writing to and Reading from Compact Object Streams
This example outputs an object in compact format, using streams from the Streams package.
#include <rw/serial/RWCompactObjectOutputStreamImp.h>
#include <rw/stream/RWNativeDataToByteOutputStreamImp.h>
#include <rw/stream/RWByteToStreambufOutputStreamImp.h>
#include <fstream.h>
 
int main(void)
{
filebuf fbuf;
fbuf.open("RWCompactObjectOutputStreamImp.out",
ios::out | ios::binary);
RWByteOutputStream& tmpBStream =
RWByteToStreambufOutputStreamImp::make(fbuf);
 
RWDataOutputStream tmpDStream =
RWNativeDataToByteOutputStreamImp::make(tmpBStream);
 
RWObjectOutputStream out =
RWCompactObjectOutputStreamImp::make(tmpDStream);
 
int i = 5;
out << i;
 
return 0;
}
This example reads an object in that was output in compact form.
 
#include <rw/serial/RWCompactObjectInputStreamImp.h>
#include <rw/stream/RWNativeDataFromByteInputStreamImp.h>
#include <rw/stream/RWByteFromStreambufInputStreamImp.h>
#include <iostream.h>
#include <rw/rwfile.h>
#include <fstream.h>
 
int main(void)
{
filebuf fbuf;
if (!RWFile::Exists("RWCompactObjectOutputStreamImp.out") )
{
cout << "The file 'RWCompactObjectOutputStreamImp.out'"
<< " does not exist.\n"
<< "You must run "
<< "RWCompactObjectOuputStreamImp"
<< "before running this file." << endl;
}
 
else
{
fbuf.open("RWCompactObjectOutputStreamImp.out",
ios::in|ios::binary);
 
RWByteInputStream& tmpBStream =
RWByteFromStreambufInputStreamImp::make(fbuf);
RWDataInputStream tmpStream =
RWNativeDataFromByteInputStreamImp::make(tmpBStream);
RWObjectInputStream in =
RWCompactObjectInputStreamImp::make(tmpStream);
int i;
in >> i;
cout << i << endl;
}
return 0;
}
6.2.2.5 Writing to and Reading from Compact Object Streams With Virtual Streams
This example outputs an object in compact form using virtual streams.
 
#include <rw/serial/RWCompactObjectOutputStreamImp.h>
#include <rw/serial/RWDataToVirtualOutputStreamImp.h>
#include <rw/pstream.h>
#include <fstream.h>
 
int main(void)
{
ofstream of(
"RWCompactObjectOutputStreamImpUsingVirtualStreams.out",
ios::out);
RWpostream pstrm(of);
 
RWDataOutputStream tmpStream =
RWDataToVirtualOutputStreamImp::make(pstrm);
RWObjectOutputStream out =
RWCompactObjectOutputStreamImp::make(tmpStream);
 
int i = 10;
out << i;
 
return 0;
}
This example reads in an object, using virtual streams, that was output in compact form.
#include <rw/serial/RWCompactObjectInputStreamImp.h>
#include <rw/serial/RWDataFromVirtualInputStreamImp.h>
#include <rw/pstream.h>
#include <iostream.h>
#include <fstream.h>
 
int main(void)
{
ifstream ifstrm;
ifstrm.open(
"RWCompactObjectOutputStreamImpUsingVirtualStreams.out",
ios::in | ios::nocreate);
if (!ifstrm)
{
cout << "The file "
<< "'RWCompactObjectOutputStreamImpUsingVirtualStreams.out'"
<< " does not\nexist. "
<< "Please run "
<< "RWCompactObjectInputStreamImpUsingVirtualStreams\n"
<< "before running this file." << endl;
}
 
else
{
RWpistream pstream(ifstrm);
 
RWDataInputStream tmpStream =
RWDataFromVirtualInputStreamImp::make(pstream);
RWObjectInputStream in =
RWCompactObjectInputStreamImp::make(tmpStream);
 
int i;
in >> i;
cout << i << endl;
}
return 0;
}