Creating a Streamable Class

IMStreamable is a virtual base class that must be mixed into all classes that support polymorphic streaming. It introduces virtual read-from and write-to stream functions that all subclasses must override. To create a streamable class, you must derive from IMStreamable and implement the streaming functions.

IStreamModule provides a way to group classes together or to qualify a class name. It provides a context for objects on a stream to distinguish a class in one DLL from a class with the same name in another DLL. Typically you will define one stream module for each DLL or application. This will eliminate name conflicts and avoid cross-DLL dependencies.

Every IMStreamable class, as part of its definition, must specify the stream module to which it belongs.

StreamableDeclarationsMacro must appear as the first item inside the declaration of all concrete classes that derive from IMStreamable. Abstract classes deriving from IMStreamable must not include this macro.

StreamableDefinitionsMacro must appear in the implementation .cpp file for all concrete classes that derive from IMStreamable. It identifies the IStreamModule to which the class belongs. Abstract classes deriving from IMStreamable must not include this macro.

To declare and define a streamable class, including its streaming functions, follow these steps:

  1. Derive from IMStreamable using the macro IMSTREAMABLE. This macro will expand to the form of IMStreamable appropriate to the compiler platform.
  2. Override the streaming functions writeToStream and readFromStream with protected functions. The functions are invoked from the outside via IMStreamable::operator >>=() and <<=()
  3. Associate the class with the stream module to which the class belongs. You must define the module itself as a global variable in one of the implementation files for your program. This macro will reference the module as an extern.
  4. Implement the stream-out function for your class.
  5. Implement the stream-in function for your class.

Step 1. Derive from IMStreamable

In your class header file, include the declaration of IMSTREAMABLE:

#include <imstrmbl.hpp>

Declare your class as a subclass of IMSTREAMABLE:

class IMyClass : public IMyBase, public IMSTREAMABLE {
    StreamableDeclarationsMacro(IMyClass);

private:
    // This is class instance data that must be
    // streamed by the streaming operators.
    int           fData;
    ISomeType*    fObjectPointer;
    IAnotherType* fAnotherObjectPointer;
    IBaseType*    fAbstractBasePointer;
    ISomeType     fEmbeddedObject;
    short         fShortArray[123];
    ISomeType     fSecondVersionObject;

public:
    IMyClass();   // Normal user class ...
    virtual ~IMyClass(); 

Step 2. Override the Streaming Functions

protected:
    virtual void writeToStream(IDataStream& toWhere) const;
    virtual void readFromStream(IDataStream& fromWhere);
};

Step 3. Associate the Class with a Stream Module

Add the following line to your .cpp file:

StreamableDefinitionsMacro(IMyClass, gMyStreamModule);

Step 4. Implement the Stream-Out Functions

void
IMyClass::writeToStream(IDataStream &toWhere) const
{
    // Stream Frame local variable. Scope must enclose the writing of all
    // data by this stream out operator.
    IStreamOutFrame myFrame(toWhere); 

    // Stream out the base class. 
    // Base class streaming is the one case where
    // writeToStream is used directly, instead of operator >>=
    IMyBase::writeToStream(toWhere);

    // Stream out the data fields.
    fData >>= toWhere; // A primitive type (an int).
    *fObjectPointer >>= toWhere; // fObjectPointer must refer to
                                 // a valid object. This streams the
                                 // object monomorphically.

    // Stream this object monomorphically. The type parameter is 
    // the actual type of the object being streamed. 
    // Handle nil pointers correctly. 
    ::writeObject(fAnotherObjectPointer, toWhere);

    // Always stream this object polymorphically.
    ::writeObject(fAbstractBasePointer, toWhere);

    // Embedded objects always use the streaming operators.
    // They don't need, and can't use, the nil pointer test
    // and heap allocation performed by read or write object.
    fEmbeddedObject >>= toWhere;

    // Write the array of shorts. 
    toWhere.writeShorts(fShortArray, 123);

    // This object was added to the stream format after the initial
    //release.
    fSecondVersionObject >>= toWhere;
};

Step 5. Implement the Stream-In Function

void
IMyClass::readFromStream(IDataStream &fromWhere)
{
    // Create a local instance of IStreamInFrame. 
    IStreamInFrame myStreamFrame(fromWhere); 

    // Stream in the base class.
    IMyBase::readFromStream(fromWhere);

    // Stream in the data fields.
    fData <<= fromWhere; // A primitive type (an int).
    *fObjectPointer <<= fromWhere; // fObjectPointer must refer
                                   // to a valid object.

    // Monomorphically stream in an object.
    // Handle nil pointers correctly.
    delete fAnotherObjectPointer;
    ::readObject(fAnotherObjectPointer, fromWhere,
        IAnotherType::staticTypeRepresentation);

    // Polymorphic readObject. Stream always specifies
    // the actual type of object to read.
    delete fAbstractBasePointer;
    ::readObject(fAbstractBasePtr, fromWhere);
    fEmbeddedObject <<= fromWhere;

    // Read the array of shorts. Must know the size and have
    // storage available before calling any array read function.
    fromWhere.readShorts(fShortArray, 123);

    // fSecondVersionObject may or may not be on the stream
    // depending on which version of the class wrote to the
    // stream.
    if (!myStreamFrame.atEnd()) {
        fSecondVersionObject <<= fromWhere;
    }
};


Introduction to the Streaming Classes
Aliased Objects
Monomorphic Streaming
Polymorphic Streaming
Release-to-Release Data Compatibility (RRDC)
Data Streams
Application Data Interfaces


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