Topic: virtual inheritance and stairway to heaven


Author: chris@alofi.etca.fr (Christian Millour)
Date: 1995/06/07
Raw View
In article <D9nrIw.9tC@ucc.su.OZ.AU>, maxtal@Physics.usyd.edu.au (John Max Skaller) writes:
|> In article <KRISS.95Jun2124153@cao-vlsi.ibp.fr>,
|> Christophe GROSJEAN <kriss@cao-vlsi.ibp.fr> wrote:
|> >
|> >   In article <1995Feb26.164710.26168@rcmcon.com>, rmartin@rcmcon.com (Robert Martin) writes:
[deletia -- refer to former posts]
|> >   |>
|> >   |> Actually, as long as your abstract classes are "perfectly abstract"
|> >   |> i.e. they have no data members.  Then virtual inheritance is not
|> >   |> needed.
|>
|> >   Is this last sentence correct ?
|>
|>  Absolutely NOT.
|>

Really sure ? See below.

[more deletia]
|>
|> >   |> >           Element   SomeConcreteType
|> >   |> >            /  \       /
|> >   |> >          v/    \v    /
|> >   |> >          /      \   /
|> >   |> >        Node    ElementImpl
|>
|>  It is crucial that "Node" and "ElementImpl" refer to
|> the SAME base subobject "Element". This is because a dispatch
|> via the pure virtual "f()" of "Element" from a method of "Node"
|> must dispatch to the overriding method of "ElementImpl".
|> If you do not use virtual inheritance here you get:
|>
|>  Element  Element      SomeConcreteType
|>    |    |         /
|>  Node  ElementImpl
|>    \...... ......./
|>           V
|>   MostDerived
|>
|> and you cannot instantiate because the class is abstract --
|> the LHS "Element" base's "f()" method is not overriden.
|>

isn't it then sufficient to override further in NodeImpl as
  Whatever NodeImpl::f() {return ElementImpl::f();}
(Ugly but instance sizes keep reasonable)

|> The whole point of this kind of structure is to perform what is
|> called a sibling call -- Node calls ElementImpl::f() via Element,
|> the _common_ base.
|>

unless the final overrider for f() happens to reside in NodeImpl below.

--chris@etca.fr

|> --
|>         JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
|>  Maxtal Pty Ltd,
|>         81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
|>         NSW 2037, AUSTRALIA     Phone: 61-2-566-2189





Author: Raoul De Kezel <raoul.de.kezel@infoboard.be>
Date: 1995/06/04
Raw View
chris@alofi.etca.fr (Christian Millour) wrote:
>In article <1995Feb26.164710.26168@rcmcon.com>, rmartin@rcmcon.com (Robert Martin) writes:
>|> robp@wv.mentorg.com (rob_pavey@mentorg.com) writes:
>|>
>|> >           Element   SomeConcreteType
>|> >            /  \       /
>|> >          v/    \v    /
>|> >          /      \   /
>|> >        Node    ElementImpl
>|> >        / \        /
>|> >      v/   \v     /
>|> >      /     \    /
>|> >   State    NodeImpl
>|> >      \        /
>|> >       \v     /
>|> >        \    /
>|> >      StateImpl
>|>
>|> >where v means virtual inheritance.
>|>
>|> >I have implemented such a hierarchy and it seems to work quite well. (At least
>|> >it does with g++ 2.6. Sun C++ 4.0.1 doesn't handle it).
>|>
>|> >It has the advantage that I can change the implementation without even
>|> >recompiling the client code and also allows alternative implementations of the
>|> >interface that work over some IPC mechanism for example (using the Proxy
>|> >pattern).
>|>
>|> Right.  This is a pattern that I call "Stairway to Heaven".  Notice
>|> how the abstactions are independent of the details, and how the
>|> details depend upon the abstractions.  This is a solid dependency
>|> structure and affords plenty of flexibility and reuse.
>|>
>|> >So... does anyone knows of any pitfalls of this approach?  The only
>|> >one I have come across is that the use of virtual inheritance makes
>|> >it difficult to downcast. RTTI should solve this though.
>|>
>|> Actually, as long as your abstract classes are "perfectly abstract"
>|> i.e. they have no data members.  Then virtual inheritance is not
>                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>|> needed.
>   ^^^^^^
>Is this last sentence correct ? It makes a huge difference in the size
>of instances, so I wish it were (Currently g++ 2.6.3 says no, but cfront
>3.0.2 accepts it).
>
>Thanks for your time,
>
>--chris@etca.fr

It seems to me that Element default constructor better had no side effect.
Also, one might have to be careful with this.

I would be happy to learn the answer when "perfectly abstract" means interface
only, no data member and no behaviour.

--- Raoul De Kezel







Author: maxtal@Physics.usyd.edu.au (John Max Skaller)
Date: 1995/06/04
Raw View
In article <KRISS.95Jun2124153@cao-vlsi.ibp.fr>,
Christophe GROSJEAN <kriss@cao-vlsi.ibp.fr> wrote:
>
>   In article <1995Feb26.164710.26168@rcmcon.com>, rmartin@rcmcon.com (Robert Martin) writes:
>   |> robp@wv.mentorg.com (rob_pavey@mentorg.com) writes:
>   |>
>   |> >           Element   SomeConcreteType
>   |> >            /  \       /
>   |> >          v/    \v    /
>   |> >          /      \   /
>   |> >        Node    ElementImpl
>   |> >        / \        /
>   |> >      v/   \v     /
>   |> >      /     \    /
>   |> >   State    NodeImpl
>   |> >      \        /
>   |> >       \v     /
>   |> >        \    /
>   |> >      StateImpl
>   |>
>   |> >where v means virtual inheritance.
>   |>
>   |> Actually, as long as your abstract classes are "perfectly abstract"
>   |> i.e. they have no data members.  Then virtual inheritance is not
>   |> needed.

>   Is this last sentence correct ?

 Absolutely NOT.

>It makes a huge difference in the size
>   of instances, so I wish it were (Currently g++ 2.6.3 says no, but cfront
>   3.0.2 accepts it).

 Yes. There is a cost to the functional power.

 I have an exception heirarchy with about 10 abstract classes
and some implementations. About 3 pages or so of text.

 The object module is 1/2 megabyte -- all RTTI and virtual
tables. The objects themselves use more space than the data members.

>Well, in a word, if you can't tell the difference between virtual and
>not virtual inheritance, you can use any type, your program will
>do the same thing. So don't put data menbers in base classes,
>don't rely on adress of base objects, and it's OK.

 NO. It isn't. Use of virtual inheritance in the "Stairway
to Heaven" is mandatory.

>   |> >           Element   SomeConcreteType
>   |> >            /  \       /
>   |> >          v/    \v    /
>   |> >          /      \   /
>   |> >        Node    ElementImpl

 It is crucial that "Node" and "ElementImpl" refer to
the SAME base subobject "Element". This is because a dispatch
via the pure virtual "f()" of "Element" from a method of "Node"
must dispatch to the overriding method of "ElementImpl".
If you do not use virtual inheritance here you get:

 Element  Element      SomeConcreteType
   |    |         /
 Node  ElementImpl
   \...... ......./
          V
  MostDerived

and you cannot instantiate because the class is abstract --
the LHS "Element" base's "f()" method is not overriden.

The whole point of this kind of structure is to perform what is
called a sibling call -- Node calls ElementImpl::f() via Element,
the _common_ base.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189





Author: kriss@cao-vlsi.ibp.fr (Christophe GROSJEAN)
Date: 1995/06/02
Raw View
In article <3qjs1c$61i@etca.etca.fr> chris@alofi.etca.fr (Christian Millour) writes:

   Path: jussieu.fr!u-psud.fr!etca.fr!alofi!chris
   From: chris@alofi.etca.fr (Christian Millour)
   Newsgroups: comp.std.c++
   Date: 1 Jun 1995 07:59:40 GMT
   Organization: Site Experimental en Hyperparallelisme - E.T.C.A. - FRANCE
   Lines: 48
   Sender: chris@alofi (Christian Millour)
   Distribution: world
   NNTP-Posting-Host: alofi.etca.fr

   In article <1995Feb26.164710.26168@rcmcon.com>, rmartin@rcmcon.com (Robert Martin) writes:
   |> robp@wv.mentorg.com (rob_pavey@mentorg.com) writes:
   |>
   |> >           Element   SomeConcreteType
   |> >            /  \       /
   |> >          v/    \v    /
   |> >          /      \   /
   |> >        Node    ElementImpl
   |> >        / \        /
   |> >      v/   \v     /
   |> >      /     \    /
   |> >   State    NodeImpl
   |> >      \        /
   |> >       \v     /
   |> >        \    /
   |> >      StateImpl
   |>
   |> >where v means virtual inheritance.
   |>
   |> >I have implemented such a hierarchy and it seems to work quite well. (At least
   |> >it does with g++ 2.6. Sun C++ 4.0.1 doesn't handle it).
   |>
   |> >It has the advantage that I can change the implementation without even
   |> >recompiling the client code and also allows alternative implementations of the
   |> >interface that work over some IPC mechanism for example (using the Proxy
   |> >pattern).
   |>
   |> Right.  This is a pattern that I call "Stairway to Heaven".  Notice
   |> how the abstactions are independent of the details, and how the
   |> details depend upon the abstractions.  This is a solid dependency
   |> structure and affords plenty of flexibility and reuse.
   |>
   |> >So... does anyone knows of any pitfalls of this approach?  The only
   |> >one I have come across is that the use of virtual inheritance makes
   |> >it difficult to downcast. RTTI should solve this though.
   |>
   |> Actually, as long as your abstract classes are "perfectly abstract"
   |> i.e. they have no data members.  Then virtual inheritance is not
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |> needed.
   ^^^^^^
   Is this last sentence correct ? It makes a huge difference in the size
   of instances, so I wish it were (Currently g++ 2.6.3 says no, but cfront
   3.0.2 accepts it).

Think of the difference between virtual inheritance and normal one.
ie : how to write a program that can detect if you use normal or
virtual inheritance. There is two way to do it, store values in
data members of base classes using both inheritance path, and check
those values. Then you know if it's physically the same object or not.

If you have no data member in base class ("perfectly abstract class")
You can yet tell the difference in some cases by taking the adresses
of the derived object casted to both base classes. It should be the
same adress with virtual inheritance, with normal inheritance
it's implementation dependant.

Well, in a word, if you can't tell the difference between virtual and
not virtual inheritance, you can use any type, your program will
do the same thing. So don't put data menbers in base classes,
don't rely on adress of base objects, and it's OK.





Author: chris@alofi.etca.fr (Christian Millour)
Date: 1995/06/02
Raw View
An email from Robert Martin today cleared some misconceptions.
My problem was that the following code wouldn't compile with g++
(but would with cfront 3.0.2)

// interface
struct VA             {virtual int a() = 0;};
struct VB : public VA {virtual int b() = 0;};
struct VC : public VB {virtual int c() = 0;};

// implementation
struct CO                        {int o() {return 0;}};
struct CA : public VA, public CO {int a() {return 1;}};
struct CB : public VB, public CA {int b() {return 2;}};
struct CC : public VC, public CB {int c() {return 3;}};

void foo() {
  CC c; // g++ complains that VA::a() (twice) and VB::b() are abstract
}

1,$s/public V/public virtual V/g and all is fine (except for
a significant increase in the size of instances).

If I understand the WP correctly g++ is correct since there
is no `final overrider' for VA::a() in CC. the solution then
is to explicitly override all pure virtuals:

struct CB : public VB, public CA {
  int a() {return CA::a();} // must override :-(
  int b() {return 1;}
};
struct CC : public VC, public CB {
  int a() {return CB::a();} // must override :-(
  int b() {return CB::b();} // must override :-(
  int c() {return 1;}
};

I'd like a confirmation that I really *must* override...

In article <KRISS.95Jun2124153@cao-vlsi.ibp.fr>, kriss@cao-vlsi.ibp.fr (Christophe GROSJEAN) writes:
|> Think of the difference between virtual inheritance and normal one.
|> ie : how to write a program that can detect if you use normal or
|> virtual inheritance. There is two way to do it, store values in
|> data members of base classes using both inheritance path, and check
|> those values. Then you know if it's physically the same object or not.
|>
|> If you have no data member in base class ("perfectly abstract class")
|> You can yet tell the difference in some cases by taking the adresses
|> of the derived object casted to both base classes. It should be the
|> same adress with virtual inheritance, with normal inheritance
|> it's implementation dependant.
|>
|> Well, in a word, if you can't tell the difference between virtual and
|> not virtual inheritance, you can use any type, your program will
|> do the same thing. So don't put data menbers in base classes,
|> don't rely on adress of base objects, and it's OK.

Very interesting. Now, since I would use this pattern to provide
a purely abstract interface to a perfectly hidden implementation,
I would have to use pointers or references to the abstract interface.
Are there more gotchas waiting for me there, or is it a safe enough
`reliance on address of base objects' ? Should I incur the space and
runtime penalties of virtual inheritance ?

thanks for your time,

--chris@etca.fr  // stolen quote of the day: "an other day, an other gotcha"










Author: chris@alofi.etca.fr (Christian Millour)
Date: 1995/06/01
Raw View
In article <1995Feb26.164710.26168@rcmcon.com>, rmartin@rcmcon.com (Robert Martin) writes:
|> robp@wv.mentorg.com (rob_pavey@mentorg.com) writes:
|>
|> >           Element   SomeConcreteType
|> >            /  \       /
|> >          v/    \v    /
|> >          /      \   /
|> >        Node    ElementImpl
|> >        / \        /
|> >      v/   \v     /
|> >      /     \    /
|> >   State    NodeImpl
|> >      \        /
|> >       \v     /
|> >        \    /
|> >      StateImpl
|>
|> >where v means virtual inheritance.
|>
|> >I have implemented such a hierarchy and it seems to work quite well. (At least
|> >it does with g++ 2.6. Sun C++ 4.0.1 doesn't handle it).
|>
|> >It has the advantage that I can change the implementation without even
|> >recompiling the client code and also allows alternative implementations of the
|> >interface that work over some IPC mechanism for example (using the Proxy
|> >pattern).
|>
|> Right.  This is a pattern that I call "Stairway to Heaven".  Notice
|> how the abstactions are independent of the details, and how the
|> details depend upon the abstractions.  This is a solid dependency
|> structure and affords plenty of flexibility and reuse.
|>
|> >So... does anyone knows of any pitfalls of this approach?  The only
|> >one I have come across is that the use of virtual inheritance makes
|> >it difficult to downcast. RTTI should solve this though.
|>
|> Actually, as long as your abstract classes are "perfectly abstract"
|> i.e. they have no data members.  Then virtual inheritance is not
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|> needed.
   ^^^^^^
Is this last sentence correct ? It makes a huge difference in the size
of instances, so I wish it were (Currently g++ 2.6.3 says no, but cfront
3.0.2 accepts it).

Thanks for your time,

--chris@etca.fr