Example Two: Restoring Polymorphically
The second example shows how the polymorphically saved collection of the first example can be read back in and faithfully restored using the overloaded extraction operator:
Rwvistream& operator>>(RWvistream&, RWCollectable&);
In this example, persistence happens when the program executes the statement:
istr >> collection2;
This statement uses the overloaded extraction operator to isomorphically restore the collection saved by the first example into collection2.
How does persistence happen? For each pointer to an
RWCollectable-derived object restored into
collection2 from the input stream
istr, the extraction operator
operator>> calls a variety of overloaded extraction operators and persistence functions. For each
RWCollectable-derived object pointer,
collection2's extraction operators:
Read the stream
istr to discover the type of the
RWCollectable-derived object.
Read the stream
istr to see if the
RWCollectable-derived object that is pointed to has already been restored and referenced in the restore table.
If the RWCollectable-derived object has not yet been restored, the extraction operators create a pointer, create an object of the correct type from the heap, and initialize the created object with data read from the stream. Then the operators update the pointer with the address of the new object, and finally save a reference to the object in the restore table.
If the RWCollectable-derived object has already been restored, the extraction operators create a pointer and read the reference to the object from the stream. Then the operators use the reference to get the object's address from the restore table, and update the pointer with this address.
Finally, the restored pointer is inserted into the collection.
We will look at the implementation details for the persistence mechanism again in
Example Two Revisited. You should note, however, that when a heterogeneous collection (which must be based on
RWCollection) is restored, the restoring process does not know the types of objects it will be restoring. Hence, it must
always allocate the objects off the heap. This means that
you are responsible for deleting the restored contents. This happens at the end of the example, in the expression
collection2.clearAndDestroy.
Here is the listing of the example:
#include <rw/ordcltn.h>
#include <rw/collstr.h>
#include <rw/collint.h>
#include <rw/tools/ctdatetime.h>
#include <rw/pstream.h>
#include <iostream>
int main ()
{
RWpistream istr(std::cin);
RWOrdered collection2;
// Even though this program does not need to have prior
// knowledge of exactly what it is restoring, the linker
// needs to know what the possibilities are so that the
// necessary code is linked in for use by RWFactory.
// RWFactory creates RWCollectable objects based on
// class ID's.
RWCollectableInt exemplarInt;
RWCollectableDateTime exemplarDateTime;
// Read the collection back in:
istr >> collection2;
// Note: The above statement is the code that restores
// the collection. The rest of this example shows us
// what is in the collection.
// Create a temporary string with value "George"
// in order to search for a string with the same value:
RWCollectableString temp("George");
// Find a "George":
// collection2 is searched for an occurrence of a
// string with value "George".
// The pointer "g" will point to such a string:
RWCollectableString* g;
g = (RWCollectableString*)collection2.find(&temp);
// "g" now points to a string with the value "George"
// How many occurrences of g are there in the collection?
size_t georgeCount = 0;
size_t stringCount = 0;
size_t integerCount = 0;
size_t dateCount = 0;
size_t unknownCount = 0;
// Create an iterator:
RWOrderedIterator sci (collection2);
RWCollectable* item;
// Iterate through the collection, item by item,
// returning a pointer for each item:
while (0 != (item = sci())) {
// Test whether this pointer equals g.
// That is, test for identity, not just equality:
if (item->isA() == __RWCOLLECTABLESTRING && item==g)
georgeCount++;
// Count the strings, dates and integers:
switch (item->isA()) {
case __RWCOLLECTABLESTRING:
stringCount++;
break;
case __RWCOLLECTABLEINT:
integerCount++;
break;
case __RWCOLLECTABLEDATETIME:
dateCount++;
break;
default:
unknownCount++;
break;
}
}
// Output results:
std::cout << "There are:\n\t"
<< stringCount << " RWCollectableString(s)\n\t"
<< integerCount << " RWCollectableInt(s)\n\t"
<< dateCount << " RWCollectableDateTime(s)\n\t"
<< unknownCount << " other RWCollectable(s)\n\n"
<< "There are "
<< georgeCount
<< " pointers to the same object \"George\"" << std::endl;
// Delete all objects created and return:
collection2.clearAndDestroy();
return 0;
}
Program Output:
There are:
2 RWCollectableString(s)
1 RWCollectableInt(s)
1 RWCollectableDateTime(s)
0 other RWCollectable(s)
There are 2 pointers to the same object "George"
Figure 8 illustrates the collection created in the first example and restored in the second. Notice that both the memory map and the data types are identical in the saved and restored collection.