asc@concurrent.co.uk (Andy Chittenden) (12/14/89)
The attached code demonstrates a problem when using C++ v2 virtual inheritence. We have a Collection class that can hold pointers to instances of class Object (or derived from Object). In the example, I have declared two collections. One is a collection of Widgets and the other is a collection of OtherWidgets. I can use my collection of Widgets as a collection of Widgets. I can add Widgets to the collection and I can get Widgets out of it. However, I can only use my collection of OtherWidgets as a collection of Objects. I can add OtherWidgets to the collection but can only get Objects out of it as OtherWidget virtually inherits from Object even though I only add OtherWidgets to it. Thus if I use virtual inheritence, I must reimplement my collection to be a collection of OtherWidgets. I have thus lost a lot of my code reuse. Has anyone else discovered this problem and if so, have they found a way out of it? Rgds, Andy Chittenden Program follows: class Object { public: Object(); }; class Collection : public Object { public: Collection(); unsigned long add_member(const Object* new_member); // returns pos of newly added member Object* get_Nth(unsigned long position); }; class Widget : public Object { public: Widget(); }; class OtherWidget : public virtual Object { public: OtherWidget(); }; main() { Widget x_widget; Collection x_widget_collection; x_widget_collection.add_member(&x_widget); Widget* widget_ptr; widget_ptr = (Widget *) x_widget_collection.get_Nth(1); OtherWidget x_otherwidget; Collection x_other_widget_collection; x_other_widget_collection.add_member(&x_otherwidget); OtherWidget* other_widget_ptr; other_widget_ptr = (OtherWidget *) x_other_widget_collection.get_Nth(1); // does not compile } "coll.cxx", line 40: error: cast: Object* ->derived OtherWidget*; Object is virtual base 1 error
jimad@microsoft.UUCP (Jim Adcock) (12/19/89)
In article <952@sl10c.concurrent.co.uk> asc@concurrent.co.uk (Andy Chittenden) writes: >..... Thus if I use virtual inheritence, I must >reimplement my collection to be a collection of OtherWidgets. I have >thus lost a lot of my code reuse. > >Has anyone else discovered this problem and if so, have they found a way >out of it? /********* Regards generic container classes given virtual base classes -- I beleive the following approach is not too slimey. The idea is: 1) Make an empty non-virtual base class "Void" which classes to be containerized inherit off of as well as virtual base class "Object" [this is the most fundamental part of the trick] 2) Make helper classes of type "DirvRef", for each derived class "Dirv" where the function of "DirvRef" is to define explicetly what conversions are allowed. (For all classes to be containerized) 3) Make a generic container class based on VoidRef. 4) Derive class specific container classes from the generic container class to make sure you put a legitimate type of object in the container in the first place [alternately: make a polymorphic container and check types at run time. [bleh]] (Most of this code could be [hack] parameterized to reduce the amount of keystrokes involved) See Lippman pages 360-370 for a good explanation for virtual base classes. Remember, a virtual base class is *always* represented as a pointer to the shared data structure for the virtual base class. So converting some object to its virtual base class is an [acceptible] many-to-one relationship. Trying to convert back from a virtual base class to the containing object is an [unacceptible] one-to-many relationship. See Lippman's "Panda" example on page 367. *********/ #include <stdio.h> #include "new.h" // foundation classes ------------------------------- typedef int handle; class Void { /* I'm empty, and I don't *do* 'anything' */ public: Void(); }; Void::Void(){} class Object { char* whoAmI; public: Object(char* id="Object"); virtual void Print() const { printf("%s\n", whoAmI); } }; Object::Object(char* id) { whoAmI = id; } class VoidRef { protected: Void* pvoid; public: VoidRef(); VoidRef(Void& obj); VoidRef(const VoidRef& obrefref); operator Void&() const { return *pvoid; } }; VoidRef::VoidRef(){pvoid = 0;} VoidRef::VoidRef(Void& obj) { pvoid = &obj; } VoidRef::VoidRef(const VoidRef& obrefref) { pvoid = obrefref.pvoid; } //pretend the Collection class is actually something substantial.... class Coll : private Void, public Object { //bogus, limit of 100, no checks. VoidRef ref[100]; VoidRef* pref; public: Coll(); handle Add(const VoidRef& new_member); VoidRef& operator[](handle position) const; }; handle Coll::Add(const VoidRef& new_member) { *pref++ = new_member; return pref-ref-1; } VoidRef& Coll::operator[](handle position) const { return ref[position]; } Coll::Coll() : Object("Coll") { pref = ref; } // application-oriented classes --------------------- class Widget : public Void, public Object { friend class WidgetRef; public: Widget(char* id="Widget"); }; Widget::Widget(char* id) : Object(id) {} class WidgetRef : public VoidRef { friend class Widget; public: WidgetRef(const Widget& widget); WidgetRef(const VoidRef& voidref); operator Widget&() const { return *(Widget*)pvoid; } }; WidgetRef::WidgetRef(const Widget& widget) : VoidRef((Void&)widget) {} WidgetRef::WidgetRef(const VoidRef& voidref) : VoidRef(voidref) {} class WidgetColl : public Coll { public: WidgetColl(); handle Add(const WidgetRef& widgetref) { return Coll::Add(widgetref); } WidgetRef operator[](handle position) const { return (Coll::operator[](position)); } }; WidgetColl::WidgetColl(){} class VBCWidget : public Void, public virtual Object { friend class VBCWidgetRef; public: VBCWidget(char* id="VBCWidget"); }; VBCWidget::VBCWidget(char* id) : Object(id) {} class VBCWidgetRef : public VoidRef { friend class VBCWidget; public: VBCWidgetRef(const VBCWidget& vbcwidget); VBCWidgetRef(const VoidRef& voidref); operator VBCWidget&() const { return *(VBCWidget*)pvoid; } }; VBCWidgetRef::VBCWidgetRef(const VBCWidget& vbcwidget) : VoidRef((Void&)vbcwidget) {} VBCWidgetRef::VBCWidgetRef(const VoidRef& voidref) : VoidRef(voidref) {} class VBCWidgetColl : public Coll { public: VBCWidgetColl(); handle Add(const VBCWidgetRef& vbcwidgetref) { return Coll::Add(vbcwidgetref); } VBCWidgetRef operator[](handle pos) const { return (Coll::operator[](pos)); } }; VBCWidgetColl::VBCWidgetColl(){} main() { Widget aWidget("aWidget"); WidgetColl aWidgetColl; handle aWidgetHandle = aWidgetColl.Add(aWidget); Widget& aWidgetRef = aWidgetColl[aWidgetHandle]; aWidgetRef.Print(); VBCWidget aVBCWidget("aVBCWidget"); VBCWidgetColl aVBCWidgetColl; handle aVBCWidgetHandle = aVBCWidgetColl.Add(aVBCWidget); VBCWidget& aVBCWidgetRef = aVBCWidgetColl[aVBCWidgetHandle]; aVBCWidgetRef.Print(); }