// $Revision: 1.3 $ // Copyright (c) 1994-1995 Taligent, Inc. All rights reserved. #ifndef TaligentSamples_STREAMEXAMPLE #include "StreamExample.h" #endif #ifndef Taligent_ASSERTIONS #include #endif #ifndef SAMPLE_NEWVERSION // The Taligent type system requires that type information be provided for each // class that may be Flattened and Resurrected, or that may be instantiated // via CreateNewObject. Here TaligentTypeExtensionMacro is used to define // this information. Some other macros also define this information, most // notably the MCollectible macros, and if they are used, this should not. // Also note that such classes must be placed into a library, not a binary. // If a class is in a binary, it may not be flattened and resurrected. As // a general rule, all classes should be in a library. TaligentTypeExtensionMacro(TStreamExample) // Taligent conventions specify that textVal should be passed via reference since // we're not going to be keeping a reference to it. Instead, we're going to adopt // a copy created using the global template function Copy. TStreamExample::TStreamExample(int intVal, EEnum enumVal, const TText& textVal) : fInt(intVal), fEnum(enumVal), fText(::Copy(textVal)), fCache(NIL) { } // The fText variable from source is not shared. TStreamExample::TStreamExample(const TStreamExample& source) : fInt(source.fInt), fEnum(source.fEnum), fText(::CopyPointer(source.fText)), fCache(NIL) { Assertion(fText != NIL); } TStreamExample::~TStreamExample() { delete fText; delete fCache; } TStreamExample& TStreamExample::operator=(const TStreamExample& source) { if (&source != this) { delete fText; fText = NIL; delete fCache; fCache = NIL; fInt = source.fInt; fEnum = source.fEnum; fText = ::CopyPointer(source.fText); } return *this; } // A typical method of streaming is to write out member variables to the stream. // 1) A version tag is streamed out using the global function WriteVersion. // 2) Base class streaming operators are called (none in this case). // 3) Standard C types are streamed. 'Int' is not portable so it is coerced to // unsigned long before streaming. // 4) Enums are streamed. They must be coerced to a portable C type sufficiently // large to fit all values of the enum. Here an unsigned char is used. // 5) Objects held polymorphically are flattened using the global template function // Flatten. Since we don't know what subclass of TText this is, we must // flatten it polymorphically. // 6) The object fCache is internal data only so it is not streamed. // 7) The stream is returned as the result. TStream& TStreamExample::operator>>=(TStream& toStream) const { ::WriteVersion(toStream, kOriginalVersion); (unsigned long)fInt >>= toStream; (unsigned char)fEnum >>= toStream; ::Flatten(fText, toStream); return toStream; } // Operator<<= is written to match operator>>=. // // 1) The version is read from the stream. The global function ReadVersion // will throw the exception TInvalidVersionError if the streamed in // version does not fall within the provided minimum and maximum values. // Since we are only compatible with one version, we don't need to pay // attention to the result-- if no exception was thrown, we know what it is. // 2) Since fInt was streamed out as an unsigned long, this is what we read in. // 3) Since fEnum was streamed out as an unsigned char, this is what we read in. // 4) The streaming operator is not a constructor, so fText has been initialized. // You also may not assume that the object is in the state left by the default // constructor-- it is generally permitted to stream into an object at any time. // Thus you have to delete fText before streaming into it. You also must NIL // fText in case an exception is encountered when streaming it in, so that our // destructor does not attempt to delete it a second time. Using TAllocationHeap // ensures that the resurrected object is in the same heap as this one-- an // important consideration if this object might ever reside in a shared heap. // 5) The cache was not streamed out, but presumably it has been invalidated by // the stream in, so it is deleted and NIL'd. // 6) The stream is returned as the result. TStream& TStreamExample::operator<<=(TStream& fromStream) { ::ReadVersion(fromStream, kOriginalVersion, kOriginalVersion); unsigned long temp1; temp1 <<= fromStream; fInt = temp1; unsigned char temp2; temp2 <<= fromStream; fEnum = (EEnum)temp2; delete fText; fText = NIL; ::Resurrect(fText, fromStream, TAllocationHeap(this)); delete fCache; fCache = NIL; return fromStream; } const TText* TStreamExample::GetText() const { return fText; } // In order to resurrect an object, it must have a default constructor (one that // takes no parameters). A protected constructor is provided for this purpose. TStreamExample::TStreamExample() : fInt(0), fEnum(kOne), fText(NIL), fCache(NIL) { } #else // This is the new version of the class TStreamExample. TaligentTypeExtensionMacro(TStreamExample) TStreamExample::TStreamExample(int intVal, const TText& textVal) : fInt(intVal), fID(), fText(::Copy(textVal)), fCache(NIL) { } TStreamExample::TStreamExample(const TStreamExample& source) : fInt(source.fInt), fID(source.fID), fText(::CopyPointer(source.fText)), fCache(NIL) { Assertion(fText != NIL); } TStreamExample& TStreamExample::operator=(const TStreamExample& source) { if (&source != this) { delete fText; fText = NIL; delete fCache; fCache = NIL; fInt = source.fInt; fID = source.fID; fText = ::CopyPointer(source.fText); } return *this; } TStreamExample::~TStreamExample() { delete fText; delete fCache; } // We write out the new version of our data. TStream& TStreamExample::operator>>=(TStream& toStream) const { ::WriteVersion(toStream, kVersion2); (unsigned long)fInt >>= toStream; fID >>= toStream; ::Flatten(fText, toStream); return toStream; } // Operator<<= is written to match operator>>=. // // 1) The version is read from the stream. Here we need to note the version, // since we are compatible with the previous version. // 2) We wish to remain backwards compatible with kOriginalVersion, so we handle // the data written by that version. We read in the enum streamed by that // version and ignore it, and establish a new id. Alternatively, we could use // the old value to mint the new id, depending in the semantics of how the // id is used. // 3) If we're reading our current version, we read it in as usual. TStream& TStreamExample::operator<<=(TStream& fromStream) { VersionInfo info = ::ReadVersion(fromStream, kOriginalVersion, kVersion2); switch (info) { case kOriginalVersion: { unsigned long temp1; temp1 <<= fromStream; fInt = temp1; unsigned char temp2; temp2 <<= fromStream; // ignore old fEnum value since it is no longer used. fID = TGlobalID(); // mint new id since it does not exist in old version data. delete fText; fText = NIL; ::Resurrect(fText, fromStream, *TAllocationHeap(this)); delete fCache; fCache = NIL; } break; case kVersion2: { unsigned long temp1; temp1 <<= fromStream; fInt = temp1; fID <<= fromStream; delete fText; fText = NIL; ::Resurrect(fText, fromStream, TAllocationHeap(this)); delete fCache; fCache = NIL; } break; default: break; } return fromStream; } const TText* TStreamExample::GetText() const { return fText; } TStreamExample::TStreamExample() : fInt(0), fID(), fText(NIL), fCache(NIL) { } #endif // SAMPLE_NEWVERSION