thomasw@hpcupt1.HP.COM (Thomas Wang) (06/03/90)
There is a serious flaw in C++'s multiple inheritance scheme. The manual reads "One can cast from a derived class to a virtual base class, but not from a virtual base class to a derived class." Normally, one would make a list class that operates on the base class. With the run-time type information, it is safe to cast to a derived class. This no longer works with multiple inheritance. The base class is usually declared 'public virtual'. So even when it is obviously safe to type cast upward, C++ still cannot do it. Reading from manual, I understand this limitation is due to the use of one way pointers. However, I wonder how much effort would be required to implement the backward pointer. Maybe we can split the vtbl into two parts. Part 1 is the function jump table indexed by function. Part 2 is the byte offset table indexed by type. This way, the base class can use the byte offset table to calculate the backward pointer. -Thomas Wang (Everything is an object.) wang@hpdmsjlm.cup.hp.com thomasw@hpcupt1.cup.hp.com
shankar@hpclscu.HP.COM (Shankar Unni) (06/05/90)
> There is a serious flaw in C++'s multiple inheritance scheme. The manual > reads "One can cast from a derived class to a virtual base class, but not > from a virtual base class to a derived class." > > Normally, one would make a list class that operates on the base class. > With the run-time type information, it is safe to cast to a derived class. > This no longer works with multiple inheritance. The base class is usually > declared 'public virtual'. So even when it is obviously safe to type cast > upward, C++ still cannot do it. I don't know about base classes being "usually" public virtual. Multiple inheritance is a very powerful and unwieldy tool. Virtual inheritance is the most tricky part of the whole setup to use - it should be used only in those cases where a base class defines some unique property for an object as a whole, and can be derived along *more than one path*. Remember the last part: you should not be using "virtual" (mainly for efficiency considerations) unless the last condition is true. The reason why you cannot go back from a virtual base class to the derived class is mainly the above point: you could have got to the base class element along more than one path. Even if, in your heirarchy, there is only one way to get from the base to the derived class, you still cannot do this safely. Besides, there is always the implementation issue: Since, when using virtual base classes, the derived class never has an exact image of any intermediate class (between the base class and this derived class), this operation my be impossible. Consider: class A {}; class B : public virtual A {}; class C : public virtual A {}; class D : public B, public C {}; Given an object of class D, there is no single contiguous chunk of it that matches the layout of a C object. ONE LAST POINT: Remember that you can always use explicit casts, but you will get really unexpected results in the above case... ----- Shankar Unni E-Mail: Hewlett-Packard California Language Lab. Internet: shankar@hpda.hp.com Phone : (408) 447-5797 UUCP: ...!hplabs!hpda!shankar
kgorlen@sparkler.dcrt.nih.gov (Keith Gorlen) (06/07/90)
In article <58170024@hpclscu.HP.COM> shankar@hpclscu.HP.COM (Shankar Unni) writes: >> There is a serious flaw in C++'s multiple inheritance scheme. The manual >> reads "One can cast from a derived class to a virtual base class, but not >> from a virtual base class to a derived class." >> >> Normally, one would make a list class that operates on the base class. >> With the run-time type information, it is safe to cast to a derived class. >> This no longer works with multiple inheritance. The base class is usually >> declared 'public virtual'. So even when it is obviously safe to type cast >> upward, C++ still cannot do it. > >I don't know about base classes being "usually" public virtual. > >Multiple inheritance is a very powerful and unwieldy tool. Virtual >inheritance is the most tricky part of the whole setup to use - it should >be used only in those cases where a base class defines some unique property >for an object as a whole, and can be derived along *more than one path*. > >Remember the last part: you should not be using "virtual" (mainly for >efficiency considerations) unless the last condition is true. Or *might* be true! This is what I encountered in making the NIH Class Library, which follows a Smalltalk-80 -like tree, support MI. Even though the library classes are organized in a tree and don't use MI themselves, a user of the library *may* wish to multiply inherit from two or more library classes. To permit this, Object (the root class of the tree), must be a virtual base class, for example. From what I've done and heard about MI so far, it looks like virtual base classes are frequently needed. Jerry Schwarz, author of the AT&T iostream library (an early application of C++ MI), even remarked a while back that he thought virtual should be the *default* for base class inheritance! It's too bad it is so tricky to use. >The reason why you cannot go back from a virtual base class to the derived >class is mainly the above point: you could have got to the base class >element along more than one path. Even if, in your heirarchy, there is >only one way to get from the base to the derived class, you still cannot do >this safely. Each class in the NIH Class Library implements the member function castdown() to cast an Object* to a pointer to a derived class. It uses run-time type information to exhaustively search an object's inheritance DAG to find the (unique) occurrence of the desired derived class, and calls a member function of that class which returns its "this" pointer as a void*. It would be better (more efficient) if downward casts from virtual bases were supported by C++. -- Keith Gorlen phone: (301) 496-1111 Building 12A, Room 2033 uucp: uunet!nih-csl!kgorlen National Institutes of Health Internet: kgorlen@alw.nih.gov Bethesda, MD 20892
thomasw@hpcupt1.HP.COM (Thomas Wang) (06/08/90)
/ hpcupt1:comp.lang.c++ / shankar@hpclscu.HP.COM (Shankar Unni) / 2:46 pm Jun 4, 1990 / > There is a serious flaw in C++'s multiple inheritance scheme. The manual > reads "One can cast from a derived class to a virtual base class, but not > from a virtual base class to a derived class." > > Normally, one would make a list class that operates on the base class. > With the run-time type information, it is safe to cast to a derived class. > This no longer works with multiple inheritance. The base class is usually > declared 'public virtual'. So even when it is obviously safe to type cast > upward, C++ still cannot do it. I don't know about base classes being "usually" public virtual. Multiple inheritance is a very powerful and unwieldy tool. Virtual inheritance is the most tricky part of the whole setup to use - it should be used only in those cases where a base class defines some unique property for an object as a whole, and can be derived along *more than one path*. Remember the last part: you should not be using "virtual" (mainly for efficiency considerations) unless the last condition is true. The reason why you cannot go back from a virtual base class to the derived class is mainly the above point: you could have got to the base class element along more than one path. Even if, in your heirarchy, there is only one way to get from the base to the derived class, you still cannot do this safely. Besides, there is always the implementation issue: Since, when using virtual base classes, the derived class never has an exact image of any intermediate class (between the base class and this derived class), this operation my be impossible. Consider: class A {}; class B : public virtual A {}; class C : public virtual A {}; class D : public B, public C {}; Given an object of class D, there is no single contiguous chunk of it that matches the layout of a C object. ONE LAST POINT: Remember that you can always use explicit casts, but you will get really unexpected results in the above case... ----- Shankar Unni E-Mail: Hewlett-Packard California Language Lab. Internet: shankar@hpda.hp.com Phone : (408) 447-5797 UUCP: ...!hplabs!hpda!shankar ----------
thomasw@hpcupt1.HP.COM (Thomas Wang) (06/09/90)
/ hpcupt1:comp.lang.c++ / shankar@hpclscu.HP.COM (Shankar Unni) / 2:46 pm Jun 4, 1990 / >> There is a serious flaw in C++'s multiple inheritance scheme. The manual >> reads "One can cast from a derived class to a virtual base class, but not >> from a virtual base class to a derived class." > class A {}; > class B : public virtual A {}; > class C : public virtual A {}; > class D : public B, public C {}; Looking at the NIH code, I found that it uses a work-around to solve the casting problem. The work-around is slow and ugly. /* virtual */ char* D::cast_down(const char& the_id) { if (&the_id == & A::id) return (char*) (A*) this; if (&the_id == & B::id) return (char*) (B*) this; if (&the_id == & C::id) return (char*) (C*) this; if (&the_id == & D::id) return (char*) (D*) this; return (char*) 0; } A* ptr = list.dequeue(); ((C*) ptr->cast_down(C::id))->member_function_of_c(); There are two things bad about this: (1) The multiple test for 'id' can be avoided if the compiler handles it. The compiler can put the byte offset in an table to be accessed by the virtual cast function. (2) The source file of 'D' must be re-written every time a super-type of 'D' changes inheritance property. -Thomas Wang (Everything is an object.) wang@hpdmsjlm.cup.hp.com thomasw@hpcupt1.cup.hp.com
kgorlen@sparkler.dcrt.nih.gov (Keith Gorlen) (06/10/90)
In article <7050019@hpcupt1.HP.COM> thomasw@hpcupt1.HP.COM (Thomas Wang) writes:
->/ hpcupt1:comp.lang.c++ / shankar@hpclscu.HP.COM (Shankar Unni) / 2:46 pm Jun 4, 1990 /
->>> There is a serious flaw in C++'s multiple inheritance scheme. The manual
->>> reads "One can cast from a derived class to a virtual base class, but not
->>> from a virtual base class to a derived class."
->
->
->> class A {};
->> class B : public virtual A {};
->> class C : public virtual A {};
->> class D : public B, public C {};
->
->Looking at the NIH code, I found that it uses a work-around to solve the
->casting problem. The work-around is slow and ugly.
Sorry, it's the best general scheme I've been able to come up with. I
don't think it would be too difficult to "memoize" the castdown
function; that is, record the "this" pointer offset in a table keyed
by base class id and derived class id so you'd only have to do the
exhaustive search of the inheritance DAG once for each type of
castdown. I haven't done this because it falls in the catagory of
"premature optimization".
->/* virtual */ char* D::cast_down(const char& the_id)
->{
-> if (&the_id == & A::id) return (char*) (A*) this;
-> if (&the_id == & B::id) return (char*) (B*) this;
-> if (&the_id == & C::id) return (char*) (C*) this;
-> if (&the_id == & D::id) return (char*) (D*) this;
-> return (char*) 0;
->}
->
->A* ptr = list.dequeue();
->((C*) ptr->cast_down(C::id))->member_function_of_c();
->
->There are two things bad about this:
->
->(1) The multiple test for 'id' can be avoided if the compiler handles it.
->The compiler can put the byte offset in an table to be accessed by the virtual
->cast function.
Yes, the compiler should really handle this. I'm hoping the
usefulness and "ugly" implementation of castdown() in the NIH Class
Library will encourage this addition to C++.
->(2) The source file of 'D' must be re-written every time a super-type of 'D'
->changes inheritance property.
Not true. The source file of 'D' references only its *immediate*
ancestors, so it must be modified only when these change ('B' and 'C'
in this case). Also, the castdown() function is automatically
generated for the single inheritance case. Writing the castdown()
function for a classes with multiple bases is highly stylized and
trivial -- it's just that it's a nuisance.
--
Keith Gorlen phone: (301) 496-1111
Building 12A, Room 2033 uucp: uunet!nih-csl!kgorlen
National Institutes of Health Internet: kgorlen@alw.nih.gov
Bethesda, MD 20892