[comp.lang.c++] Disk save/restore of derived class objects

laporta@apollo.uucp (John X. Laporta) (08/28/87)

    Derived classes with virtual functions pose a problem for disk save and restore.
Standard software techniques allow easy transformation of  memory objects into disk 
objects, but the value of the _vptr member of derived classes in which virtual 
functions are defined is program-specific in environments where the class library 
is bound in with the program. This complicates save and restore considerably, since
a _vptr written by one program is invalid when read by another.

    One technique for solving this problem follows. Consider this pair of classes:

        class base {
            int member;
        public:
            virtual void set_member(){member = 1;}
        };

        class derived:public base {
            int other_member();
        public:
            void set_member(){member = other_member;}
        };

Class derived now has a member _vptr whose type is undefined. If program A writes
copies of objects of class derived to disk, and program B reads these copies in,
any call to derived::set_member() can cause serious problems, because the _vptr
member that was valid for program A will almost certainly be invalid for program B.

Add a derived type enumeration, a type field in class base, and a friend
function void base_set_vptr(base*) of class base as follows:

        int enum class_type { base_type, derived_type};

        class base {
            int type;
            int member;

        friend void base_set_vptr(base*);
        public:
            virtual void set_member(){member = 1};
        };

Add the appropriate assignment to the type field to constructors for the base and
derived classes. Then define void base_set_vptr(base*) as follows:

        void base_set_vptr(base* new_base)
        {
            static derived *derived_dummy;

            if (NULL == derived_dummy)                    // on first call only
                derived_dummy = new derived;
            if (derived_type == new_base->type)           // reset _vptr if any
                new_base->_vptr = derived_dummy->_vptr;
        }

When saving a copy of class derived to disk, employ another friend function,
void base_null_vptr(base*):

        void base_null_vptr(base* new_base)
        {
            if (derived_type == new_base->type)
                new_base->_vptr = NULL;
        }

and two copies of the same instance of class derived written by different programs
will be identical on disk.

It would be possible to have one friend function:

        void base_set_vptr(base* new_base, <type_of_vptr> ptr)

if the type of _vptr were known, but as B. Stroustrup says on pages 202-203:

        Clearly implementing [virtual functions] will involve storing some kind of
        type information in each object of class employee. The space taken (in the
        *current implementation*) is just enough to hold one pointer.

(Italics mine.) It appears Stroustrup is not guaranteeing against change in
the type of the _vptr member. Isolating the assignment in these friend functions
should minimize the pain of transition between implementations.

Does anyone know a simpler or better solution?

keith@nih-csl.UUCP (keith gorlen) (08/29/87)

In article <36ed1da2.c449@apollo.uucp>, laporta@apollo.uucp (John X. Laporta) writes:
> 
>     Derived classes with virtual functions pose a problem for disk save and restore.
> Standard software techniques allow easy transformation of  memory objects into disk 
> objects, but the value of the _vptr member of derived classes in which virtual 
> functions are defined is program-specific in environments where the class library 
> is bound in with the program. This complicates save and restore considerably, since
> a _vptr written by one program is invalid when read by another.
> 
>     One technique for solving this problem follows. ...

[Technique that involves explicit manipulation of the _vptr member described]

> Does anyone know a simpler or better solution?

I think so.  My Object-Oriented Program Support (OOPS) class library
implements storeOn()/readFrom() functions similar to those of
SmallTalk-80 for storing arbitrary data structures comprised of OOPS
objects on an ostream and reconstituting them from an istream.  As you
correctly point out, one program's _vptr is, in general, invalid in
another program's address space, and must therefore be converted into
something portable.  You are also correct in being concerned about a
technique that relies on the specific implementation of virtual
functions, since this will change if/when multiple inheritance is
added to C++.  (Bjarne has written a paper describing an
implementation of this).

The technique used by OOPS is described briefly in the chapter "C++
Under UNIX" which has just been published in *The UNIX Papers*, I have
written a detailed description that will be published (eventually) in
*Software -- Practice and Experience*, and the code is in the public
domain, so I won't go into details here.  Basically, when an OOPS
object is stored, the name (in ASCII) of its class is stored along
with the values of its member variables.  Each OOPS class implements
a constructor function that takes an istream and a pointer as arguments,
reads the member variable values from the istream, and constructs an
instance of its class at the specified address.  Executing this constructor
function in the context of the reading process does whatever is necessary
to setup the virtual function binding in an implementation independent manner.
The reading process must also be able to relate the name of the class of an
object to be read to the appropriate constructor, which is handled in OOPS
by an instance of class Dictionary that is setup by static constructors.

At first you might think that storing the class name for each object
would consume an excessive amount of space, but it need not be so bad
because the class name can be stored just once for each class needed
by a data structure, and all the instances of each class can share the
same class name.

To get a copy of the "guru-only" version of OOPS, just send me a
letter on your company or university letterhead requesting the
"Object-Oriented Program Support (OOPS) class library for C++" and a
reel of 1/2" mag tape (a small reel will suffice).  I'll return the
tape with a copy of OOPS recorded on it in tar format at 1600 bpi, and
I'll include a copy of the (incomplete) OOPS reference manual.

Please include your uucp address in the letter.
-- 
	Keith Gorlen			phone: (301) 496-5363
	Building 12A, Room 2017		uucp: seismo!elsie!nih-csl!keith
	National Institutes of Health
	Bethesda, MD 20892