Topic: extends" keyword for pseudo-virtual inheritance?


Author: "Ben Cox" <benc@comanage.net>
Date: Sat, 9 Sep 2000 01:31:34 GMT
Raw View
Suppose I have the following set of classes.


 class IA
 {
 public:
     virtual void AMethod() = 0;
 };


 class AImpl : public IA
 {
 public:
     virtual void AMethod()
     { cout << "AImpl::AMethod()" << endl; }
 };


So far, so good.  IA is essentially an interface; AImpl is a class
which implements that interface.

Now, I want to extend my system.  I define another interface IB,
descended from IA, and I want to define another class BImpl which
implements IB, and inherits its implementation of IA from AImpl.

The straightforward method of doing this is:


 class IB : public IA
 {
 public:
     virtual void BMethod() = 0;
 };


 class BImpl : public AImpl, public IB
 {
 public:
     virtual void BMethod()
     { cout << "BImpl::BMethod()" << endl; }
 };


The problem with this, as astute readers will realize, is that the
class BImpl is still abstract.  Despite the fact that we actually have
an implementation of all of the relevant methods available to us, they
are not disambiguated.  Specifically, the class BImpl inherits two
AMethod()s, and one of them (the one it inherited from IB) is still
pure virtual.  (I believe) BImpl has two vtables; the BImpl vtable
that inherits from IB has an empty slot for AMethod() and hence BImpl
is abstract.

Now, I could declare the class BImpl as follows:


 class BImpl : public AImpl, public IB
 {
 public:
     virtual void BMethod()
     { cout << "BImpl::BMethod()" << endl; }

     // The ugly bit:
     virtual void AMethod()
     { AImpl::AMethod(); }
 };


but doing so is ugly to me for two reasons.  (1) I want AMethod to be
truly inherited, not just delegated to a chosen base class.  (2) A
call to AMethod() on a BImpl object involves an extra delegation
step, with an extra stack frame, etc.  (Although I can imagine that a
particularly clever compiler could optimize the extra stack frame
away.)


    *** Aside ***

    Now, if all these inheritances were virtual, there would be no
    difficulty.  If I compile these example classes with virtual
    inheritance, Microsoft Visual C++ 6.0 (sp3) on Windows 2000 SP1
    does exactly what I want, with only an informational warning
    (C4250) that it is picking AImpl::AMethod in BImpl via dominance.
    Great; that's what I want.

    Unfortunately, though, in my case IA and IB are actually COM
    interfaces, and COM won't allow virtual interface inheritance.
    (At least, the MIDL compiler can't be made to do it as far as I
    know, and I can't see how it would be binary-compatible across all
    compilers, so I can't see how COM *could* allow it.)


What I'd really like is for the compiler to notice the following
things:

  * BImpl derives from AImpl and from IB.

  * AImpl and IB share a base class (IA), and AImpl has
    implementations for all of the IA methods which IB is missing.

  * BImpl itself declares and implements the remaining IB methods.

  * It would be dandy to fill in the missing vtable entries in BImpl's
    IB vtable with the corresponding entries from AImpl.  (And I want
    the compiler to do this.)

Since what I _really_really_want_ is interface inheritance from IB
with implementation inheritance from AImpl.

I understand that doing this in all cases would likely break some
existing programs.

What if, instead, we had a new keyword?  Let's call it "extends".
Used thus:


 class BImpl : extends AImpl, public IB
 {
 public:
     virtual void BMethod()
     { cout << "BImpl::BMethod()" << endl; }
 };


What I'd like the "extends" keyword to do is the following.

  * When building vtables for class BImpl, and we come to a slot
    which would otherwise be empty:

  * And the method in question inherits from a class which is a common
    base class with another base class of BImpl:

  * And there is exactly one such other base class which both
    implements the method and is inherited using "extends":

  * Fill in the vtable slot in question with the method from the other
    base class.


Comments?

Is there another way to get BImpl to be non-abstract without using
either virtual inheritance or explicit redeclaration of the IA method
and delegation to the AImpl base class?

Thanks in advance for any comments or suggestions.


__
Ben Cox <BenC@CoManage.net>, Senior Development Engineer
CoManage Corporation -- Customer-Aware Managed Services
http://www.comanage.net -- http://jobs.comanage.net


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Johan Wikman" <johan.wikman@nokia.com>
Date: Mon, 11 Sep 2000 18:24:34 GMT
Raw View
"Ben Cox" <benc@comanage.net> wrote in message
news:fEfu5.2486$qq3.56165@typhoon2.ba-dsg.net...
> [...]
> Is there another way to get BImpl to be non-abstract without using
> either virtual inheritance or explicit redeclaration of the IA method
> and delegation to the AImpl base class?

Yes - provide the implementations of the interfaces with templates:

 template<class T>
 class AImpl : public T {
 public:
   void AMethod() {
     cout << "AImpl::AMethod()" << endl;
   }
 };

 template<class T>
 class BImpl : public AImpl<T> {
 public:
   void BMethod()
   {
     cout << "BImpl::BMethod()" << endl;
   }
 };

Then you use them the following way:

 IA *pA = new AImpl<IA>;
 IB *pB = new BImpl<IB>;

In cases like this I usually name the templates with a trailing
underscore (AImpl_ and BImpl_) and then typedef nicer names.

 typedef AImpl_<IA> AImpl;
 typedef BImpl_<IB> BImpl;

--
johan.wikman@nokia.com


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Michiel Salters <salters@lucent.com>
Date: Mon, 11 Sep 2000 17:59:34 GMT
Raw View
Ben Cox wrote:

> Suppose I have the following set of classes.

>  class IA  {
>  public:
>      virtual void AMethod() = 0;
>  };

>  class AImpl : public IA  {
>  public:
>      virtual void AMethod()
>      { cout << "AImpl::AMethod()" << endl; }
>  };

> So far, so good.  IA is essentially an interface; AImpl is a class
> which implements that interface.

Which leads me to question: if IA is-a interface, and AImpl is-a
implementation of that, i.e has-a IA interface, why do you use
(public) inheritance to model AImpl is-a IA?

> Now, I want to extend my system.  I define another interface IB,
> descended from IA, and I want to define another class BImpl which
> implements IB, and inherits its implementation of IA from AImpl.

I think this is OK. BImpl is a valid replacment for AImpl, i.e is-a.
The problem with BImpl is-a IB has the same issue again, though.

> The straightforward method of doing this is:

>  class IB : public IA  {
>  public:
>      virtual void BMethod() = 0;
>  };

You add an extra method, I understand.

>  class BImpl : public AImpl, public IB  {
>  public:
>      virtual void BMethod()
>      { cout << "BImpl::BMethod()" << endl; }
>  };

> The problem with this, as astute readers will realize, is that the
> class BImpl is still abstract.  Despite the fact that we actually have
> an implementation of all of the relevant methods available to us, they
> are not disambiguated.  Specifically, the class BImpl inherits two
> AMethod()s, and one of them (the one it inherited from IB) is still
> pure virtual.  (I believe) BImpl has two vtables; the BImpl vtable
> that inherits from IB has an empty slot for AMethod() and hence BImpl
> is abstract.

The vtable's might very well exist as described, the issue certainly
exists. This is correct, though. You have two interfaces, IA (via
AImpl) and IB. Each interface has its own implementation of AMethod.
You've provided only one implementation in AImpl.

> Now, I could declare the class BImpl as follows:

>  class BImpl : public AImpl, public IB  {
>  public:
>      virtual void BMethod()
>      { cout << "BImpl::BMethod()" << endl; }

>      // The ugly bit:
>      virtual void AMethod()
>      { AImpl::AMethod(); }
>  };

> but doing so is ugly to me for two reasons.  (1) I want AMethod to be
> truly inherited, not just delegated to a chosen base class.  (2) A
> call to AMethod() on a BImpl object involves an extra delegation
> step, with an extra stack frame, etc.  (Although I can imagine that a
> particularly clever compiler could optimize the extra stack frame
> away.)

The second issue isn't something I'd worry about, I fully expect this
to happen given that the implementation is provided "inline". The
first issue is more important. You seem to be confused by your
inheritance structure. You DO truly inherit one of your two AMethods.
(AImpl::AMethod) If you want the other to be identical, forward it.


>     *** Aside ***

>     Now, if all these inheritances were virtual, there would be no
>     difficulty.  If I compile these example classes with virtual
>     inheritance, Microsoft Visual C++ 6.0 (sp3) on Windows 2000 SP1
>     does exactly what I want, with only an informational warning
>     (C4250) that it is picking AImpl::AMethod in BImpl via dominance.
>     Great; that's what I want.

Well, that's another story. This might be closer to what you try
to achieve: BImpl having one IA (right?). That this happens to work
is perhaps more than luck, perhaps not. But we'll leave that to
philosophers.

>     Unfortunately, though, in my case IA and IB are actually COM
>     interfaces, and COM won't allow virtual interface inheritance.
>     (At least, the MIDL compiler can't be made to do it as far as I
>     know, and I can't see how it would be binary-compatible across all
>     compilers, so I can't see how COM *could* allow it.)

Well, that's confusing things a bit by bringing in something not
really OO into an OO problem. Still, this leaves me wondering,
why are you deriving from IA and IB? Because you want precisely
those vtables. (And using COM, we know vtables exist, and
most probably are used, too.) Your implementations of IA and
IB are nothing more than mappings, and they're not connected by
derivation. Your "true implementation" AImpl shouldn't inherit
from IA. You might use something like

template <typename Impl>
class IAmapping : public IA {
 Impl impl;
 IAmapping() { /* construct appropriate impl*/ }
 void IAmethod1() { impl.IAmethod1() };
 ...
}

etc. This shows that Impl is used to provide an interface IA, but
by itself isn't such an interface.

> What I'd really like is for the compiler to notice the following
> things:

>   * BImpl derives from AImpl and from IB.

Ergo, BImpl is-a AImpl *and* an IB ?

>   * AImpl and IB share a base class (IA), and AImpl has
>     implementations for all of the IA methods which IB is missing.

They don't share a base object, though. If IA has a member X, and
AMethod changes it, then BImpl has two X'es, and they could be
changed separately were it not for the fact that there's no
IB::AMethod().

>   * BImpl itself declares and implements the remaining IB methods.

>   * It would be dandy to fill in the missing vtable entries in BImpl's
>     IB vtable with the corresponding entries from AImpl.  (And I want
>     the compiler to do this.)

> Since what I _really_really_want_ is interface inheritance from IB
> with implementation inheritance from AImpl.

Well, implementation inheritance shouldn't be public to start with.
However, this doesn't matter. Even private methods end up in vtables.

> I understand that doing this in all cases would likely break some
> existing programs.

Very likely, yes.

> What if, instead, we had a new keyword?  Let's call it "extends".
> Used thus:

>  class BImpl : extends AImpl, public IB  {
>  public:
>      virtual void BMethod()
>      { cout << "BImpl::BMethod()" << endl; }
>  };

> What I'd like the "extends" keyword to do is the following.

>   * When building vtables for class BImpl, and we come to a slot
>     which would otherwise be empty:

>   * And the method in question inherits from a class which is a common
>     base class with another base class of BImpl:

>   * And there is exactly one such other base class which both
>     implements the method and is inherited using "extends":

>   * Fill in the vtable slot in question with the method from the other
>     base class.

> Comments?

Yes. If COM is broken, why fix C++ ?
COM appears to require single inheritance, and reserves that
inheritance for its own interface inheritance, and balks on
virtual inheritance. That's not a C++ problem.

Perhaps AImpl to have a conversion to IA* which COM could
notice? BImpl could inherit that ("using ...") and add a
conversion to IB*. Just tack it and the end of the vtable,
where proper C++ doesn't look normally.

> Is there another way to get BImpl to be non-abstract without using
> either virtual inheritance or explicit redeclaration of the IA method
> and delegation to the AImpl base class?

I don't think so. If you have one AMethod, and you need two, you're
one short. Can't think of a way around it.

Michiel Salters

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]