[comp.lang.c++] problem with virtual inheritence

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();
}