Rogue Wave Banner

Click on the banner to return to the user guide home page.

©Copyright 1996 Rogue Wave Software

More on Storing and Retrieving RWCollectables

In Chapter 14 (Polymorphic Persistance), we saw how to save and restore the morphology or pointer relationships of a class using the following global functions:

Rwvostream&  operator<<(RWvostream&, const RWCollectable&);
RWFile&      operator<<(RWFile&,     const RWCollectable&);
Rwvostream&  operator<<(RWvostream&, const RWCollectable*);
RWFile&      operator<<(RWFile&,     const RWCollectable*);
Rwvistream&  operator>>(RWvistream&, RWCollectable&);
RWFile&      operator>>(RWFile&,     RWCollectable&);
Rwvistream&  operator>>(RWvistream&, RWCollectable*&);
RWFile&      operator>>(RWFile&,     RWCollectable*&);

When working with RWCollectables, it is useful to understand how these functions work. Here is a brief description.

When you call one of the left-shift << operators for any collectable object for the first time, an identity dictionary is created internally. The object's address is put into the dictionary, along with its ordinal position in the output file_for example, first, second, sixth, etc.

Once this is done, a call is made to the object's virtual function saveGuts(). Because this is a virtual function, the call will go to the definition of saveGuts() used by the derived class. As we have seen, the job of saveGuts() is to store the internal components of the object. If the object contains other objects inheriting from RWCollectable, the object's saveGuts()calls operator<<() recursively for each of these objects.

Subsequent invocations of operator<<() do not create a new identity dictionary, but store the object's address in the already existing dictionary. If an address is encountered which is identical to a previously written object's address, then saveGuts() is not called. Instead, a reference is written that this object is identical to some previous object.

When the entire collection is traversed and the initial call to saveGuts() returns, the identity dictionary is deleted and the initial call to operator<<() returns.

The function operator>>() essentially reverses this whole process by calling restoreGuts to restore objects into memory from a stream or file. When encountering a reference to an object that has already been created, it merely returns the address of the old object rather than asking the RWFactory to create a new one.

Here is a more sophisticated example of a class that uses these features:

#include <rw/collect.h>
#include <rw/rwfile.h>
#include <assert.h>

class Tangle : public RWCollectable
{

public:

  RWDECLARE_COLLECTABLE(Tangle)

  Tangle* nextTangle;
  int     someData;

  Tangle(Tangle* t = 0, int dat = 0){nextTangle=t; someData=dat;}

  virtual void saveGuts(RWFile&) const;
  virtual void restoreGuts(RWFile&);

};

void Tangle::saveGuts(RWFile& file) const{
   RWCollectable::saveGuts(file);          // Save the base class

   file.Write(someData);                        // Save internals

   file << nextTangle;                      // Save the next link
}

void Tangle::restoreGuts(RWFile& file){
   RWCollectable::restoreGuts(file);    // Restore the base class

  file.Read(someData);                       // Restore internals

  file >> nextTangle;                    // Restore the next link
}

// Checks the integrity of a null terminated list with head "p":
void checkList(Tangle* p){
   int i=0;
   while (p)
   {
     assert(p->someData==i);
     p = p->nextTangle;
     i++;
   }
}

RWDEFINE_COLLECTABLE(Tangle, 100)

main(){
   Tangle *head = 0, *head2 = 0;

   for (int i=9; i >= 0; i--)
     head = new Tangle(head,i);

   checkList(head);                    // Check the original list

   {
     RWFile file("junk.dat");
     file << head;
   }

   RWFile file2("junk.dat");
   file2 >> head2;

   checkList(head2);                   // Check the restored list
   return 0;
}

In the above example, the class Tangle implements a circularly linked list. What happens? When function operator<<() is called for the first time for an instance of Tangle, it sets up the identity dictionary as described above, then calls Tangle's saveGuts(), whose definition is shown above. This definition stores any member data of Tangle, then calls operator<<() for the next link. This recursion continues on around the chain.

If the chain ends with a nil object (that is, if nextTangle is zero), then operator<<() notes this internally and stops the recursion.

On the other hand, if the list is circular, then a call to operator<<() is eventually made again for the first instance of Tangle, the one that started this whole chain. When this happens, operator<<() will recognize that it has already seen this instance before and, rather than call saveGuts() again, will just make a reference to the previously written link. This stops the series of recursive calls and the stack unwinds.

Restoration of the chain is done in a similar manner. A call to:

RWFile& operator>>(RWFile&, RWCollectable*&);

can create a new object off the heap and return a pointer to it, return the address of a previously read object, or return the null pointer. In the last two cases, the recursion stops and the stack unwinds.


Previous file Table of Contents Next file