gaa@castle.ed.ac.uk (Gerard A. Allan) (06/12/91)
Many C++ libraries have a common root class and as a consequence they contain many functions and methods that take the root class as an argument requiring a cast to the desired type inside the function. This can cause a problem when attempting to use multiple inheritance and a virtual base class. eg. class Root { virtual void function( Root *r)=0; }; class Derived : public Root { int x; virtual void function( Root *r); }; void Derived::function( Root *r) { Derived *n=(Derived *)r; n->x++; } if I now define Derived as, class Derived : virtual public Root { int x; virtual void function( Root *r); }; In preparation to make, class mine : virtual public Root {}; class multi : public mine, public Derived {}; The function Derived::function(Root *r) no longer works as there is an error "cannot cast up from virtual baseclass Root" This is of course perfectly true (ARM 10.6c) "Casting form a virtual base class to a derived class is disallowed to avoid requiring an implementation to maintain pointers to enclosing objects". This makes it very difficult to use multiple inheritance and virtual bases from a library with a single root class. Is there some way round this problem so that I can combine classes in a "natural" way ? After all, the cast is not ambiguous since there is only one Derived. What solutions to this problem have C++ programmers developed or is multiple inheritance incompatible with the single inheritance methodology ? And finally, is there a case for requiring "an implementation to maintain pointers to enclosing objects" ? I'd be interested in hearing how programmers have dealt with this problem. Gerard A. Allan | Post: EMF gaa@castle.ed.ac.uk | Kings Buildings JANET:gaa@uk.ac.ed.castle | University of Edinburgh Internet:gaa%castle.ed.ac.uk@cunyvm.cuny.edu | Edinburgh EARN/BITNET:gaa%castle.ed.ac.uk@UKACRL | Scotland UUCP:gaa%castle.ed.ac.uk@ukc.uucp | EH9 3JL
sakkinen@jyu.fi (Markku Sakkinen) (06/14/91)
(Arrrgh: I already wrote and submitted something like this yesterday, but some testing of the news system here caused all articles to get lost.) In article <10971@castle.ed.ac.uk> gaa@castle.ed.ac.uk (Gerard A. Allan) writes: > >Many C++ libraries have a common root class and as a consequence they >contain many functions and methods that take the root class as an >argument requiring a cast to the desired type inside the function. >This can cause a problem when attempting to use multiple inheritance and >a virtual base class. >eg. > class Root { > virtual void function( Root *r)=0; > }; > class Derived : public Root { > int x; > virtual void function( Root *r); > }; > void Derived::function( Root *r) > { > Derived *n=(Derived *)r; > n->x++; > } > >if I now define Derived as, > class Derived : virtual public Root { > int x; > virtual void function( Root *r); > }; > >In preparation to make, > class mine : virtual public Root {}; > class multi : public mine, public Derived {}; > >The function Derived::function(Root *r) no longer works as there is an >error "cannot cast up from virtual baseclass Root" > >This is of course perfectly true (ARM 10.6c) "Casting form a virtual >base class to a derived class is disallowed to avoid requiring an >implementation to maintain pointers to enclosing objects". This >makes it very difficult to use multiple inheritance and virtual bases >from a library with a single root class. Have you realised how utterly dangerous your 'Derived::function' is in the first place? From one viewpoint it is a blessing that virtual base classes prevent such casts, which can cause any amount of havoc. There is no assurance that actual arguments will in fact refer to Derived objects, unless 'function' is redefined in no other class in your whole programme. More object-oriented languages than C++ (Simula, Eiffel, ...) typically enforce run-time checking of such casts to make them safe; in C++ such checking is not even possible. It was a deliberate (and in my opinion unfortunate) desing decision in C++ that all objects do not carry sufficient type information like they do in those others languages. Your problem is aggravated by the fact that it is hard to think of any sensible example of multiple public inheritance with _non-virtual_ base classes. Exceptions, of course, are those base classes that are not inherited over more than one path by any non-immediate descendant. >Is there some way round this problem so that I can combine classes in a >"natural" way ? After all, the cast is not ambiguous since there is only >one Derived. What solutions to this problem have C++ programmers >developed or is multiple inheritance incompatible with the single >inheritance methodology ? And finally, is there a case for requiring >"an implementation to maintain pointers to enclosing objects" ? With type information in each object, there would be no need for the restriction. Eiffel seems to have no sweat with situations like this. In C++ there is hardly any other solution than to hand-code an additional level of object-orientation and be sure to use it in all appropriate classes. I suppose some of the large general-purpose C++ class libraries have been built that way. ---------------------------------------------------------------------- "All similarities with real persons and events are purely accidental." official disclaimer of news agency New China Markku Sakkinen (sakkinen@jytko.jyu.fi) SAKKINEN@FINJYU.bitnet (alternative network address) Department of Computer Science and Information Systems University of Jyvaskyla (a's with umlauts) PL 35 SF-40351 Jyvaskyla (umlauts again) Finland ----------------------------------------------------------------------
pena@brainware.fi (Olli-Matti Penttinen) (06/17/91)
In article <1991Jun14.113736.3795@jyu.fi> sakkinen@jyu.fi (Markku Sakkinen) writes: In article <10971@castle.ed.ac.uk> gaa@castle.ed.ac.uk (Gerard A. Allan) writes: > >Many C++ libraries have a common root class and as a consequence they >contain many functions and methods that take the root class as an >argument requiring a cast to the desired type inside the function. >This can cause a problem when attempting to use multiple inheritance and >a virtual base class. [ ex. deleted ] >This is of course perfectly true (ARM 10.6c) "Casting form a virtual >base class to a derived class is disallowed to avoid requiring an >implementation to maintain pointers to enclosing objects". This >makes it very difficult to use multiple inheritance and virtual bases >from a library with a single root class. Have you realised how utterly dangerous your 'Derived::function' is in the first place? From one viewpoint it is a blessing that virtual base classes prevent such casts, which can cause any amount of havoc. There is no assurance that actual arguments will in fact refer to Derived objects, unless 'function' is redefined in no other class in your whole programme. Yes, libraries like NIH, which *VERY* much depend on up-casting are indeed troublesome when combined with multiple inheritance (MI). They always enforce the user to follow some additional protocol to make the classes work with the rest of the world. To circumvent (sp?) the problem, there are very few alternatives with current C++ implementations. The best approach I've found is to view non-virtual derivation as inclusion of the base object (with added complexity due to virtual functions, of course) and virtual derivation as indirectly including (referring to) a common instance of a base class object. TODO: the client code should use its natural type hierarchies to define the appropriate classes, but instead of deriving from (say NIH) existing classes with the aforementioned behavior a pointer to such an object should be defined as a private member. Another problem arises: who should construct the subobject? The best answer is found in ARM 12.6.2: the most derived class is responsible. At times it is somewhat difficult to ensure at compile time that any given class is indeed a leaf class. An additional boolean member could be included to indicate whether the (conseptually) virtual base object has been initialized. Also, watch out for assignment! Most (all, I think) current implementations incorrectly assign virtual base objects multiple times (once per occurence). Sometimes it only degrades performance, at other times can be disastrous. More object-oriented languages than C++ (Simula, Eiffel, ...) typically enforce run-time checking of such casts to make them safe; in C++ such checking is not even possible. It was a deliberate (and in my opinion unfortunate) desing decision in C++ that all objects do not carry sufficient type information like they do in those others languages. There has been (and still is) discussion on the subject. On the one hand, run time type information would be desirable, not only for typesafe runtime polymorhism but for high level debugging purposes, as well. On the other hand, any additional hidden overheads definitely are not in "the spirit of C", which C++ tries to honor. >Is there some way round this problem so that I can combine classes in a >"natural" way ? After all, the cast is not ambiguous since there is only >one Derived. What solutions to this problem have C++ programmers >developed or is multiple inheritance incompatible with the single >inheritance methodology ? And finally, is there a case for requiring >"an implementation to maintain pointers to enclosing objects" ? With type information in each object, there would be no need for the restriction. Eiffel seems to have no sweat with situations like this. In C++ there is hardly any other solution than to hand-code an additional level of object-orientation and be sure to use it in all appropriate classes. I suppose some of the large general-purpose C++ class libraries have been built that way. A better way to solve the problem altogether would be templates. With them, the libraries could be got right in the first place. Code size would of course increase because of numerous duplications, but that shouldn't be such a problem with current virtual memory techniques. At least that would clearly be the method of choice from a large scale software engineering viewpoint, whatever that may be :-) ==pena -- Olli-Matti Penttinen <pena@brainware.fi> | "When in doubt, use brute force." Brainware Oy | --Ken Thompson P.O.Box 330 +---------------------------------- 02151 ESPOO, Finland Tel. +358 0 4354 2565 Fax. +358 0 461 617