8.4. Writing/Reading Base Objects to/from AbstractPersistenceStream

To create a class of Base objects that supports persistence and that can be stored and restored using the SaveBase and LoadBase modules (see Section 8.2, “Composing, Storing and Retrieving Base Objects”), the following steps can be taken (this is an alternative to the TreeNode persistence mechanism):

The following example shows how to implement persistence to a simple class SegmentedObject. The class SegmentedObject is derived from BaseItem which is derived from Base:

Base (abstract class, no members)
     |
BaseItem (name, id)
     |
SegmentedObject (objectGrayValue, voxelCount, boundingBox)

Example 8.2. How to Implement Persistence for Base Objects

Header file of a class SegmentedObject:

class SegmentedObject : public BaseItem {

...

public:
      //! announce supported persistence interfaces
      virtual bool implementsPersistence(PersistenceInterface iface) const
      {
          return (iface == PersistenceByStream);
      }

      //! Implement export functionality (as used by the SaveBase module):
      virtual void writeTo(AbstractPersistenceOutputStream* stream) const;

      // Set current version number
      ML_SET_ADDSTATE_VERSION(1);

      //! Implement import functionality (as used by the LoadBase module):
      virtual void readFrom(AbstractPersistenceInputStream* stream, int version);

private:
      //! ML runtime system related stuff
      ML_CLASS_HEADER(SegmentedObject);

      // Members to be (re-)stored:
      //! The identifying gray value of this object
      long _objectGrayValue;

      //! Number of voxels
      long _voxelCount;

      //! Bounding box respective to original image
      SubImageBox* _boundingBox;

...

}


Source file of class SegmentedObject:

Writing the object state to the stream:

//! Implement export functionality:
void SegmentedObject::writeTo(AbstractPersistenceOutputStream* stream) const
{
  // Add superclass members:
  ML_WRITETO_SUPER(BaseItem, stream);

  // Add this class' members:
  stream->write(_objectGrayValue, "ObjectGrayValue");
  stream->write(_voxelCount, "VoxelCount");

  // The bounding box is optional, do not write if the pointer is NULL:
  if (_boundingBox) {
    // start a new sub-structure
    stream->startStruct("BoundingBox");
    stream->write(_boundingBox->v1, "v1");
    stream->write(_boundingBox->v2, "v2");
    stream->endStruct();
  }
}

Reading the object state from the stream:

//! Implement import functionality:
void SegmentedObject::readFrom(AbstractPersistenceInputStream* stream, int version)
{
  // Read super class members:
  ML_READFROM_SUPER(BaseItem, stream);

  // Handle version differences:
  // In this example, version 0 used a different tag for _objectGrayValue
  // and did not write the VoxelCount value.
  switch (version) {
    case 0 :
      // Read object gray value from old tag name
      parent->read(_objectGrayValue, "GrayValue");
      break;
    case 1 :
      parent->read(_objectGrayValue, "ObjectGrayValue");
      break;
    default:
      // Throw exception: A version upgrade was performed without adapting the version handling
      // Note that this exception only needs to be thrown if you want to be on the safe side.
      // The persistence framework outputs a warning on its own if a newer version than that
      // from ML_SET_ADDSTATE_VERSION is encountered.
      throw PersistenceStreamFormatException("Unsupported version");
  }

  // Handle version difference (voxelCount available or not)
  // by calling the macro readOptional which sets
  // the given variable to a default value (second parameter)
  // in case the tag "VoxelCount" was not found.
  stream->readOptional(_voxelCount, 0, "VoxelCount");

  // Bounding box is optional:
  // However, startStruct can not be called optionally,
  // hence we have to check beforehand if there is an element with the correct name:
  if (stream->isNextInStruct("BoundingBox")) {
    try {
      ML_CHECK_NEW(_boundingBox, new SubImageBox());
      stream->startStruct("BoundingBox");
      stream->read(_boundingBox->v1, "v1");
      stream->read(_boundingBox->v2, "v2");
      stream->endStruct()
    }
    catch (const PersistenceStreamException& e) {
      // make sure to delete bounding box again:
      ML_DELETE(_boundingBox);
      // re-throw exception
      throw;
    }
  }
}

Registering the class in the runtime type system:

  ML_CLASS_SOURCE(SegmentedObject, BaseItem)