Xref: utzoo comp.lang.c++:5519 comp.object:413 Path: utzoo!attcan!utgpu!jarvis.csri.toronto.edu!rutgers!ucsd!usc!gem.mps.ohio-state.edu!wuarchive!wugate!uunet!cimshop!davidm From: cimshop!davidm@uunet.UU.NET (David S. Masterson) Newsgroups: comp.lang.c++,comp.object Subject: Shippable C++ Objects (RFC) Message-ID: Date: 15 Nov 89 18:10:37 GMT Sender: davidm@cimshop.UUCP Organization: Consilium Inc., Mountain View, California. Lines: 159 Request for Comments (*please post*) Based on previous discussions within these groups and some work I am currently doing, I'd like to get some comments (please post) on the following ideas for the shipment of C++ objects between processes (and, by implication, the persistence of the object). Hopefully, this will generate some interesting discussions. Below I list some ideas/requirements for the shippability between processes of general objects in C++: 1. Assume an Event interface between processes. Therefore, when shipping an object, you can be very sure that something on the other side (in this case, a callback) will understand what it is because of the event type. 2. Assume each class of objects has methods for building a shippable byte array representation of the object and returning a pointer to that shippable form. This means that each object is responsible for building a shippable form of itself. By implication, the parent of the object and contained objects can build shippable representations of themselves and, so, are called at appropriate times by the child's shippable method, which then copies the information into its shippable form. This capability MUST exist on an object for an object to be shipped (the implications for storage are similar if not the same). 3. Assume each class of objects has methods for filling (or constructing) itself from the shippable byte array representation made in (2). Since the object knows what it did to create (2), it should know what it can do to get itself back from that (2) (most likely a reverse operation). Therefore, all initilization functions would only need to take a char* (void* ??) pointer. This also implies that Event objects need only contain some header followed by an object type and an unknown length byte array. 4. All parts of an object must be handled in (2) and (3), even to the point of just deciding that part of an object doesn't need to be shipped. Therefore, even memory pointers must be resolved in the processing of (2) and (3). 5. Base type objects are copiable into shippable objects as is. Their size and type will be well-known by both sides of the shipping software, so the only thing to resolve is recognizing them in the stream of bytes. If all other aspects of shippability are resolved, then recognition is not necessary as they MUST be at well-known spots in the data stream. 6. Copying of object contents out of shippable form MUST be done with knowledge of copying the object contents into shippable form. Suggested might be that an object gets its parent shippable form, followed by top to bottom contents of itself (NOTE: next paragraph) and copies those into the shippable output form for the current object. The initializer from a shippable form would then unwind this in a similar manner. 7. Memory pointers are the special case. In the process of encountering a memory pointer, converting to shippable form seems to require a need for a symbol table. Basically, the value of the memory pointer would be given to the symbol table which would return a symbol for that memory pointer (either a new one or the previous one). If the symbol table says that this memory pointer has been seen before, then there is nothing to do other than copy the symbol (with the Seen_Before flag) into the byte stream. If the memory pointer is new to the symbol table, then the symbol is copied to the byte stream (with the New flag) and the pointer is then followed to produce the shippable form of what it points to. 8. This may lead to one extra copy of an object in the shippable stream due to the potential for a circular pointer chain. This should be resolvable by well-known methods and object design criteria. For instance, it is highly unlikely (read NEVER HAPPEN) that one object would point into the middle of another object (especially with the tendancy toward private data), so putting the memory address of each new object as a whole in the symbol table and a corresponding symbol into the byte stream should eliminate the possibility of circular reference problems in object pointer chains. That is, when processing an object, enter its memory address into the symbol table so that if any succeeding objects point to it, no further processing need be done. In the case of circular pointers from ivar to ivar within an object, they should be resolvable through the definition of the object (the designer should be able to handle it). 9. Reconstructing the object on the other side would mean nothing more than copying well-known values from the byte stream into their proper places and doing some special tracking of pointer symbols in the byte stream to make sure they get relinked properly. When a pointer type is encountered in constructing an object, a symbol had better be on the input stream (trust the transmission media). This symbol would be stripped out and the symbol table queried to determine if it has been seen before. If not, an object is new()'ed and a char* pointer to the area after the symbol in the byte stream would be passed to its init function (after the value of new() is entered in the symbol table). If it has, then the value is copied from the symbol table into the current pointer. 10. If the methods for build_shippable() and init_from_shippable() are virtual on the object, then object type for the shippable form will have to be correct even if the object referred to when build_shippable() is invoked is of pointer to parent type. Because the object type will be correct, then the proper event callback on the other side will get invoked. 11. The implication of all this is that objects that are virtual in nature and have memory pointers imbedded in them can be shipped from process to process without much work and, therefore, object instances may be passed back and forth without limitation on their representation in the C++ sense. For example: class A { class X { X *alpha; int abc, cde; } } class B:A { class Y { Y *beta; float m, n; } } class C:B { class Z { Z *gamma; char x[10]; } } would translate into: Shippable_of_C { Symbol Value Label(C); Label(C) &C Label(C); Label(C) &B Label(C); Label(C) &A Label(X); Label(X) &X Label(X); Label(Y) &Y int abc, cde; Label(Z) &Z Label(Y); Label(Y); float m, n; Label(Z); Label(Z); char x[10]; } Decomposition of this should be relatively easy. The internal format of Shippable_of_C is not important, so long as (2) and (6) are followed carefully. Note that when C enters the address of itself into the Symbol Table, the returned value will also suffice for both B and A. The two extra Label(C) values in the Shippable form are necessary, though, as the init_from_shippable() function for each object cannot make assumptions about whether it has children or not. Therefore, initializing C will strip the first Label(C) and call initialize of B (which will strip the second and so on...). Also note that when (say) B is processing the pointer to Y, it knows it is processing a pointer and, therefore, should expect Label(Y) on the input stream. It can then strip this value and enter it with the address of the Y object that it will next allocate (with new()). It should not call a constructor for Y with the values in the stream until it has entered the address of Y into the symbol table. This is in case of circular references within Y. So it calls "new Y()", enters the returned value into the symbol table, then calls "Y.init()" with the char* (void* ??) pointer to the area after the Label(Y) to get it initialized. Y will then strip the expected Label(Y) and enter the following information in the byte stream into itself. -- =================================================================== David Masterson Consilium, Inc. uunet!cimshop!davidm Mt. View, CA 94043 =================================================================== "If someone thinks they know what I said, then I didn't say it!"