Topic: Sideways" inheritance


Author: Michael McCarty <mikem@eai.com>
Date: 1998/04/06
Raw View
Hyman Rosen wrote:
>
> jkanze@otelo.ibmmail.com writes:
> > The problem is that you have three IUnknown::a which have to be
> > redefined, because you have three separate instances of IUnknown
> > in the hierarchy.  I suspect that this is NOT what you want--for
> > example, it means that you cannot cast a pointer to a MyInt up
> > to IUnknown, because the case would also be ambiguous.
>
> It's not an option to declare the IUnknown inheritance to be virtual.
> The existing interfaces that you need to implement don't inherit that
> way. You are correct about the upcast being ambiguous. In fact, an
> implementation must provide an unambiguous IUnknown pointer for the
> object - it does so by picking a particular one of the available ones
> (which doesn't matter, as long as it's always the same one) in the
> QueryInterface function.

Actually, in MS ATL at least, the IUnknown pointer it returns from QI is
always a pointer to embedded member data and has nothing to do with
inheriting from any particular interface.

>
> > The solution is to use virtual inheritance when inheriting from
> > IUnknown, so that there is only one instance of the base class in
> > the hierarchy.  (In practice, inheritance from an abstract class
> > should almost always be virtual.)
>
> I agree with you, but outside considerations constrain what can be
> done.
>
> In any case, my original question remains. Is there anything wrong
> with allowing the single non-abstract version of the function be the
> implementation of all the abstract ones? And is there anything wrong
> with having name lookup find the non-abstract version in preference
> to the abstract ones? (I suppose that the latter would detract from
> the purity of name lookup without regard to type, but C++ has plenty
> of non-beautiful things in it already.)

Your request can only possibly make sense if the base classes are truly
interfaces (i.e. no member data), otherwise references to member data in
the virtual function would result in garbage.

Since there is nothing special about interface classes as far as C++ is
concerned and considering the relative rarity of the situation, it seems
the compiler would have to go through some pretty serious work to do
something that you probably didn't intend it to do in the first place.

Keep in mind, COM is not C++ (and vice versa) -- what you are looking at
is the implementation in C++ of a specification for an interface
protocol that happens to be roughly object-oriented.  The similarities
end there.


--
--------------------------------+----------------------------------------------
Michael McCarty  (mikem@eai.com)| If you pick up a starving dog and make
Engineering Animation Inc. | him prosperous, he will not bite you;
Ames, IA  50010   | that is the principle difference between
Work: (515) 296-9908  | a dog and a man.
http://www.eai.com/             |                    Mark Twain


--Husk_of_Hares_367_000--
---
[ 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: Hyman Rosen <uunet!jyacc!hymie@ncar.UCAR.EDU>
Date: 1998/03/18
Raw View
In looking at the way MS Windows COM implements interface inheritance,
I've come to wish that C++ would prefer non-abstract functions over
abstract ones in its name lookup. Let me explain.

In programming COM, one creates interface classes which are abstract
classes with no data members. Universally, such new interface classes
inherit (non-virtually) from a base interface known as IUnknown. The
implementation of two of the three mebers of IUnknown is nearly always
the same, so it would be convenient to place them in a class in order
to inherit the implementation. What I would like to do looks like this:

struct IUnknown { virtual void a() = 0; };
struct IMyInt1 : IUnknown { ... };
struct IMyInt2 : IUnknown { ... };
struct UnkImpl : IUnknown { void a() { } };

struct MyInt : IMyInt1, IMyInt1, UnkImpl { };

I want UnkImpl::a to be the function used for any call to a()
in MyInt and its bases. This doesn't work in C++ because name
lookup finds ambiguous a's in all those IUnknown bases. MyInt
is still considered to be an abstract class. Is there any
technical reason why UnkImpl::a can't be unified with IMyInt1::a
and IMyInt2::a in the declaration of MyInt?
---
[ 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: msherman@magma.ca (Marc Sherman)
Date: 1998/03/18
Raw View
In article <t7pvjqv0ai.fsf@calumny.jyacc.com>,
Hyman Rosen  <uunet!jyacc!hymie@ncar.UCAR.EDU> wrote:
>What I would like to do looks like this:
>
>struct IUnknown { virtual void a() = 0; };
>struct IMyInt1 : IUnknown { ... };
>struct IMyInt2 : IUnknown { ... };
>struct UnkImpl : IUnknown { void a() { } };
>
>struct MyInt : IMyInt1, IMyInt1, UnkImpl { };

You can do something very similar using templates:

template<typename TBase> struct UnkImpl: public TBase
{ virtual void a() { /* impl */ } };

struct MyIntBase: IMyInt1, IMyInt2 { };
typedef UnkImpl<MyIntBase> MyInt;

If the implementation of UnkImpl::a takes much code, you can reduce
template bloat by doing this:

struct UnkImplBase
{ void aImpl(UnkImpl* thisUnkImpl) { /* impl */ } };

template<typename TBase> struct UnkImpl: public TBase, private UnkImplBase
{ friend struct UnkImplBase; virtual void a() { aImpl(this); } };

- Marc
---
[ 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: jkanze@otelo.ibmmail.com
Date: 1998/03/18
Raw View
In article <t7pvjqv0ai.fsf@calumny.jyacc.com>,
  Hyman Rosen <uunet!jyacc!hymie@ncar.UCAR.EDU> wrote:
>
> In looking at the way MS Windows COM implements interface inheritance,
> I've come to wish that C++ would prefer non-abstract functions over
> abstract ones in its name lookup. Let me explain.
>
> In programming COM, one creates interface classes which are abstract
> classes with no data members. Universally, such new interface classes
> inherit (non-virtually) from a base interface known as IUnknown. The
> implementation of two of the three mebers of IUnknown is nearly always
> the same, so it would be convenient to place them in a class in order
> to inherit the implementation. What I would like to do looks like this:
>
> struct IUnknown { virtual void a() = 0; };
> struct IMyInt1 : IUnknown { ... };
> struct IMyInt2 : IUnknown { ... };
> struct UnkImpl : IUnknown { void a() { } };
>
> struct MyInt : IMyInt1, IMyInt1, UnkImpl { };
>
> I want UnkImpl::a to be the function used for any call to a()
> in MyInt and its bases. This doesn't work in C++ because name
> lookup finds ambiguous a's in all those IUnknown bases. MyInt
> is still considered to be an abstract class. Is there any
> technical reason why UnkImpl::a can't be unified with IMyInt1::a
> and IMyInt2::a in the declaration of MyInt?

The problem is that you have three IUnknown::a which have to be
redefined, because you have three separate instances of IUnknown
in the hierarchy.  I suspect that this is NOT what you want--for
example, it means that you cannot cast a pointer to a MyInt up
to IUnknown, because the case would also be ambiguous.

The solution is to use virtual inheritance when inheriting from
IUnknown, so that there is only one instance of the base class in
the hierarchy.  (In practice, inheritance from an abstract class
should almost always be virtual.)

--
James Kanze    +33 (0)1 39 23 84 71    mailto: kanze@gabi-soft.fr
        +49 (0)69 66 45 33 10    mailto: jkanze@otelo.ibmmail.com
GABI Software, 22 rue Jacques-Lemercier, 78000 Versailles, France
Conseils en informatique orient   e objet --
              -- Beratung in objektorientierter Datenverarbeitung

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/   Now offering spam-free web-based newsreading



[ 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: Marc Girod <girod@stybba.ntc.nokia.com>
Date: 1998/03/19
Raw View
>>>>> "HR" == Hyman Rosen <uunet!jyacc!hymie@ncar.UCAR.EDU> writes:

HR> struct IUnknown { virtual void a() = 0; };
HR> struct IMyInt1 : IUnknown { ... };
HR> struct IMyInt2 : IUnknown { ... };
HR> struct UnkImpl : IUnknown { void a() { } };

HR> struct MyInt : IMyInt1, IMyInt1, UnkImpl { };

HR> I want UnkImpl::a to be the function used for any call to a()
HR> in MyInt and its bases. This doesn't work in C++ because name
HR> lookup finds ambiguous a's in all those IUnknown bases. MyInt
HR> is still considered to be an abstract class. Is there any
HR> technical reason why UnkImpl::a can't be unified with IMyInt1::a
HR> and IMyInt2::a in the declaration of MyInt?

But...

struct IUnknown { virtual void a() = 0; };
struct IMyInt1 : virtual IUnknown { ... };
struct IMyInt2 : virtual IUnknown { ... };
struct UnkImpl : virtual IUnknown { void a() { } };

struct MyInt : virtual IMyInt1, virtual IMyInt1 { };

struct MyIntHidden: virtual MyInt, virtual UnkImpl {};
MyInt* factory() { return new MyIntHidden; }

and now it works.

And your clients don't know of your implementation.
This scheme is called "virtual mixin". I believe this is _the_ way
C++ works at its best.

Best Regards!

--
Marc Girod         Nokia Telecommunications  NWS/NMS/NMS for Data
Valimo 1/2         P.O. Box 315              Phone:  +358-9-511 63331
00380 Helsinki     00045 NOKIA Group         Fax:    +358-9-511 63310
Finland                                      marc.girod@ntc.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: James Kuyper <kuyper@wizard.net>
Date: 1998/03/19
Raw View
Hyman Rosen wrote:
>
> In looking at the way MS Windows COM implements interface inheritance,
> I've come to wish that C++ would prefer non-abstract functions over
> abstract ones in its name lookup. Let me explain.
>
> In programming COM, one creates interface classes which are abstract
> classes with no data members. Universally, such new interface classes
> inherit (non-virtually) from a base interface known as IUnknown. The
> implementation of two of the three mebers of IUnknown is nearly always
> the same, so it would be convenient to place them in a class in order
> to inherit the implementation. What I would like to do looks like this:
>
> struct IUnknown { virtual void a() = 0; };
> struct IMyInt1 : IUnknown { ... };
> struct IMyInt2 : IUnknown { ... };
> struct UnkImpl : IUnknown { void a() { } };
>
> struct MyInt : IMyInt1, IMyInt1, UnkImpl { };
>
> I want UnkImpl::a to be the function used for any call to a()
> in MyInt and its bases. This doesn't work in C++ because name
> lookup finds ambiguous a's in all those IUnknown bases. MyInt
> is still considered to be an abstract class. Is there any
> technical reason why UnkImpl::a can't be unified with IMyInt1::a
> and IMyInt2::a in the declaration of MyInt?

It doesn't sound to me like you actually need multiple inheritance:

struct IMyInt : IUnknown {
 // implement the two members that are nearly always the same
 // Leave third member unimplemented.
};

struct MyInt : IMyInt {
 // Implement third member
};


[ 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              ]