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