Enabling Release-to-Release Data Compatibility

To enable RRDC for a class:

  1. The streaming functions must declare a local (stack) instance of a stream frame object, the scope of which surrounds the actual streaming of the data. This is done with IStreamInFrame and IStreamOutFrame. For a writeToStream function, the frame will cause the extent of the object's data on the stream to be tagged. For a readFromStream function, the frame will facilitate testing for the end of the object's data and skipping over data to reach the end of the object.
  2. On stream out, any extension data (additional data added to the stream format by a new release of the class) must be streamed after the original data.
  3. On stream in, a test to verify that the end of the stream data for that object has not been reached must precede the reading of any extension data. This will ensure that the stream was not written by an old class implementation that did not provide extension data. On stream in, if the object's data on the stream has not been exhausted at the end of the readFromStream function, as signaled by the stream frame object going out of scope, the remaining unread data will be skipped.

RRDC is only available when one of the tagged stream formats is used. Raw binary stream formats, which omit the tags that delimit object data on a stream, are not suitable for persistent data or for streaming between systems that may have different DLLs installed. It is up to the client that specifies the stream to choose an appropriate encoding. RRDC enabled classes will stream out their data correctly on nontagged streams, but they will be unable to detect the end of a frame on streaming in.

In the following example, the fData variable is replaced, in a later version of the class, by fOldData and fNewData.

Here is the original version of DerivedClass::writeToStream:

void
DerivedClass::writeToStream(IDataStream& toStream) const
{
    IStreamOutFrame streamContext(toStream);  // Must be declared on stack.
    BaseClass::writeToStream(toStream);       // Write base class data first.
    fData >>= toStream;                       // Write instance data to stream.
}

This is the original version of DerivedClass::readFromStream:

void
DerivedClass::readFromStream(IDataStream& fromStream)
{
    IStreamInFrame streamContext(fromStream); // Must be declared on stack
    BaseClass::readFromStream(fromStream);    // Read base class data first.
    fData <<= fromStream;                     // Read instance data from stream.
}

This is the new version of DerivedClass::writeToStream:

void
DerivedClass::writeToStream(IDataStream& toStream) const
{
    IStreamOutFrame streamContext(toStream);
    BaseClass::writeToStream(toStream);
    fOldData >>= toStream;                    // Used to be fData
    fNewData >>= toStream;
}

This is the new version of DerivedClass::readFromStream:

void
DerivedClass::readFromStream(IDataStream& fromStream) const
{
    IStreamInFrame streamContext(fromStream);
    BaseClass::readFromStream(fromStream);
    fOldData <<= fromStream;

    // fNewData may or may not be on the stream depending on
    // which version of the class wrote to the stream.
    if (!streamContext.atEnd()) {
        fNewData <<= fromWhere;
    }
    else {
        fNewData = fOldData; // The new data wasn't there so
                             // use the old data.
    }
}


Introduction to the Streaming Classes
Release-to-Release Data Compatibility (RRDC)
Data Streams
Application Data Interfaces


Adding Streaming Support to Structs and Simple Classes
Creating a Streamable Class
Creating a Streamable Template Class
Instantiating a Stream Module
Streaming Base Classes
Streaming Aliases and Aliased Objects
Streaming Objects Monomorphically
Streaming Objects Polymorphically
Transitioning from Older Streaming Constructs