Object I/O


To stream objects, use operator>> and operator<< . By streaming objects, you can store them persistently or send them between processes. This section explains how to use the basic features of binary object I/O. Later sections in this chapter describe how to add binary streaming support to your classes.

Reading and Writing Objects

To write an object, use operator<< . An encoded copy of the object, along with size and type information, are written.

To read an object into an existing destination object, use operator>> with the destination as the argument. The destination's previous value is replaced. If the type of the original object and the destination are not the same, an error is generated.

In the following example, a time and a date object are written to a file using binary object I/O. When these objects are retrieved, the second read fails due to a type mismatch.

Because the Universal Streaming Service does not require modification of the class, USS extensions to classes can be maintained in separate headers and object code files. As a convention, Recursion Software <ToolKits> place USS header files in <ospace/uss>. In the following example, <ospace/uss/time.h> is included to USS-enable the classes in Time<ToolKit>. Refer to the class catalog entry for a specific Recursion Software class to determine whether it is USS-enabled and to locate the correct header file.

Example <ospace/stream/examples/objio1.cpp>
#include <fstream>
#include <ospace/stream.h>
#include <ospace/time.h>
#include <ospace/uss/time.h>

void
write()
  {
  fstream file( "objio1.bin", ios::binary | ios::out | ios::trunc );
  os_bstream stream( os_adapter_for( file ) );

  os_time time = os_time::now();
  os_date date = os_date::today();

  cout << "Write " << time << ", " << date << endl;
  stream << time << date; // Write two objects in binary format.
  }

void
read()
  {
  fstream file( "objio1.bin", ios::binary | ios::in );
  os_bstream stream( os_adapter_for( file ) );

  os_time time;
  stream >> time; // Read an os_time into time.
  cout << "Read " << time << endl;

  os_time_and_date time_and_date;
  // Attempt to read an os_date into an os_time_and_date.
  stream >> time_and_date;
  cout << "Read " << time_and_date << endl;
  }

void
main()
  {
  os_streaming_toolkit init_streaming;
  os_time_toolkit init_time;

  try
    {
    write();
    read();
    }
  catch( os_streaming_toolkit_error& e )
    {
    cout << "Caught os_streaming_toolkit_error : " << endl;
    cout << '\t' << e.what() << endl;
    cout << '\t' << e.description( e.code() ) << endl;
    }
  }

Write 15:11:37.798917, 09/23/96
Read 15:11:37.798917
Caught os_streaming_toolkit_error :
        type_mismatch: Tried to read os_time_and_date but got os_date
           [bstream.cpp:1295]
        Attempted to read wrong type.

Reading and Writing Pointers to Objects

To read an object onto the heap, use operator>> with an appropriate pointer type as the argument. The C++ pointer compatibility rules apply. An object of a particular class can be read into the heap using a pointer to that class or any of its base classes. The pointer is set to zero if an error, end-of-input condition, or zero pointer occurs during the read.

Example <ospace/stream/examples/objio2.cpp>
#include <fstream>
#include <ospace/stream.h>
#include <ospace/time.h>
#include <ospace/uss/time.h>

void
write()
  {
  fstream file( "objio2.bin", ios::binary | ios::out | ios::trunc );
  os_bstream stream( os_adapter_for( file ) );

  os_time time = os_time::now();
  os_time_and_date time_and_date = os_time_and_date::now();

  cout << "Write " << time << ", " << time_and_date << endl;
  stream << time << time_and_date;
  }

void
read()
  {
  fstream file( "objio2.bin", ios::binary | ios::in );
  os_bstream stream( os_adapter_for( file ) );

  os_time* time;
  stream >> time; // Read an os_time into the heap.
  cout << "Read " << *time << endl;
  delete time; // Deallocate time.

  stream >> time; // ERROR, since os_time* is not an os_time_and_date*
  cout << "Read " << *time << endl;
  delete time;
  }

void
main()
  {
  os_streaming_toolkit init_streaming;
  os_time_toolkit init_time;

  try
    {
    write();
    read();
    }
  catch( os_streaming_toolkit_error& e )
    {
    cout << "Caught os_streaming_toolkit_error : " << endl;
    cout << '\t' << e.what() << endl;
    cout << `\t' << e.description( e.code() ) << endl;
    }
  }

Write 15:17:31.338124, 09/23/96 15:17:31.338493
Read 15:17:31.338124
Caught os_streaming_toolkit_error :
        type_mismatch: Cannot cast from os_time_and_date to os_time             [class.cpp:477]
        Attempted to read wrong type.

Note that the os_bstream class considers references as objects not as pointers. When reading and writing with references, they behave as object instances not as pointers.

When writing an object via a pointer, the pointed-to object is written along with size and type information, or a special indicator is written if the pointer is zero.

If an object is written via a pointer, it can only be read into another pointer on the heap using operator>> . If you write an object using a pointer and do not read it into a pointer, a runtime error occurs.

The following example illustrates how to write a zero pointer and safely read it as zero. The date2 object is written using a pointer and read into an actual instance of os_date , illustrating a type mismatch error.

Example <ospace/stream/examples/objio3.cpp>
#include <fstream>
#include <ospace/stream.h>
#include <ospace/time.h>
#include <ospace/uss/time.h>

void
write()
  {
  fstream file( "objio3.bin", ios::binary | ios::out | ios::trunc );
  os_bstream stream( os_adapter_for( file ) );

  os_time time = os_time::now();
  os_date* date1 = 0; // nil.
  os_date* date2 = new os_date( os_date::august, 21, 1968 );

  cout << "Write " << time << ", " << date1 << ", " << *date2 << endl;
  stream << &time << date1 << date2; // Write objects via pointers.
  delete date2;
  }

void
read()
  {
  fstream file( "objio3.bin", ios::binary | ios::in );
  os_bstream stream( os_adapter_for( file ) );

  os_time* time;
  stream >> time; // Read an os_time into the heap
  cout << "Read " << *time << endl;
  delete time; // Deallocate time.

  os_date* date1;
  stream >> date1; // Read nil into date1.
  cout << "Read " << date1 << endl;

  os_date date2;
  // Read os_date into date2. ERROR, since written using a ptr.
  stream >> date2;
  cout << "Read " << date2 << endl;
  }
void
main()
  {
  os_streaming_toolkit init_streaming;
  os_time_toolkit init_time;

  try
    {
    write();
    read();
    }
  catch( os_streaming_toolkit_error& e )
    {
    cout << "Caught os_streaming_toolkit_error : " << endl;
    cout << '\t' << e.what() << endl;
    cout << '\t' << e.description( e.code() ) << endl;
    }
  }

Write 15:23:17.973520, 0x0, 08/21/68
Read 15:23:17.973520
Read 0x0
Caught os_streaming_toolkit_error :
        type_mismatch: Tried to read os_date but got os_date*             [bstream.cpp:1295]
        Attempt to read wrong type.

Reading and Writing Polymorphically

An object may be written and read via a pointer to one of its base classes; the binary streaming system always performs the C++ regular type checking rules.

In the following hierarchy, the cat class inherits from the pet class.



The next example writes an instance of cat via a pointer to pet , and then reads the instance of cat into a pointer to pet . The full source code is listed later in this section.

Example <ospace/stream/examples/objio4.cpp>
#include "cat.h"
#include <fstream>
#include <ospace/stream.h>

void
write()
  {
  fstream file( "objio4.out", ios::binary | ios::out | ios::trunc );
  os_bstream stream( os_adapter_for( file ) );

  pet* my_pet = new cat; // Create a cat on the heap.
  my_pet->name_ = "Agatha";
  cout << "Write ";
  my_pet->print(); // Print the cat.
  stream << my_pet;
  delete my_pet;
  }

void
read()
  {
  fstream file( "objio4.out", ios::binary | ios::in );
  os_bstream stream( os_adapter_for( file ) );

  pet* my_pet = 0;
  stream >> my_pet; // Stream the pet onto the heap.
  cout << "Read ";
  my_pet->print(); // Print the pet.
  delete my_pet;
  }

void
main()
  {
  os_streaming_toolkit initialize;
  write();
  read();
  }

Write short hair cat called Agatha
Read short hair cat called Agatha

Morphology

When an object that contains pointers to other objects is written and restored, the object's morphology is maintained. All directly or indirectly pointed-to objects are written only once, and the object pointers are correctly restored when the object is read.

For example, consider a class called person that contains two data members. The first data member holds the person's name as a string, and the second data member holds a pointer to the person's buddy, which is another person object. If two persons are reciprocal buddies, the object graph looks like the following.



If streaming a pointer to an object precipitated a blind writing of the object, an endless loop would occur. Each person would stream a buddy until the storage medium overflowed.

To prevent this problem, Streaming<ToolKit> records information about each object as it is written. After an object is written once, subsequent writes create a special reference to the object instead of writing the object repeatedly. Special references are detected during the reading process and are processed.

The following example illustrates the way in which morphology is maintained. The address of each object is printed to show that the mutual pointer relationships are correctly restored. The full source code of person is presented later in this chapter.

Example <ospace/stream/examples/objio5.cpp>
#include "person.h"
#include <fstream>
#include <ospace/stream.h>

void
write()
  {
  fstream file( "objio5.out", ios::binary | ios::out | ios::trunc );
  os_bstream stream( os_adapter_for( file ) );

  person* p1 = new person;
  p1->name_ = "Graham";
  person* p2 = new person;
  p2->name_ = "David";
  p1->buddy_ = p2; // David is Graham's buddy.
  p2->buddy_ = p1; // Graham is David's buddy.

  stream << p1; // Stream p1 and its associated data members.

  delete p1;
  delete p2;
  }

void
read()
  {
  fstream file( "objio5.out", ios::binary | ios::in );
  os_bstream stream( os_adapter_for( file ) );

  person* p1;
  stream >> p1; // Restore person and its associated data members.
  person* p2 = p1->buddy_; // Who is p1's buddy?

  cout << "p1 = " << p1->name_ << " @ " << p1 << endl;
  cout << "buddy = " << p1->buddy_->name_ << " @ " << p1->buddy_;
  cout << endl << endl;

  cout << "p2 = " << p2->name_ << " @ " << p2 << endl;
  cout << "buddy = " << p2->buddy_->name_ << " @ " << p2->buddy_;
  cout << endl;

  delete p1;
  delete p2;
  }

void
main()
  {
  os_streaming_toolkit initialize;
  write();
  read();
  }

p1 = Graham @ 0x85de8
buddy = David @ 0x86e48

p2 = David @ 0x86e48
buddy = Graham @ 0x85de8

Morphology is also important if there are multiple references to the same object. One example is a collection of pointers with multiple identical pointers. Without correct morphology behavior in the stream, the read would produce a separate copy of the object for each duplicate pointer to that object, rather than correctly assigning all pointers to point to a single object.

The following rules describe the way in which Streaming<ToolKit> maintains morphology.

For example, if you have an object with a pointer stored in two vectors, and you stream out one vector and then the next vector, when you stream them in, the original object has a separate instance for each vector.

However, if you place both vectors in another class and stream it, the object is shared. Morphology is maintained for all objects streamed recursively as a result of a single user write.


Copyright©1994-2026 Recursion Software LLC
All Rights Reserved - For use by licensed users only.