I wish c++ did interfaces better.

  • Follow


Given an interface (in the c++ sense, nothing more than a struct =
containing pure virtual methods)

  struct Iv1 {
    virtual method0()=3D0; };

And a class that implements the interface

  class Iv1Impl : public Iv1 {
    virtual method0(){...}
  }

Its not unreasonable to create a version 2 interface

  struct Iv2 : Iv1 {
    virtual method1()=3D0;};

One might *try* to create an implementation class like this

  class Iv2Impl : public Iv2, Iv1Impl  {
    virtual method1(){...}; // implement Iv2 method=20
  };

That of course never never never works. Why not? So what that Ive =
introduced another method0()=3D0 ? Why can't it figure out it already =
has an implementation for that method that I'd really like to allow it =
to use?



0
Reply chris.becke (32) 8/6/2008 8:53:36 AM

Chris Becke wrote:
> Given an interface (in the c++ sense, nothing more than a struct containing pure virtual methods)
<...>

> That of course never never never works. Why not? 
 > So what that Ive introduced another method0()=0 ?
 > Why can't it figure out it already has an implementation
 > for that method that I'd really like to allow it to use?


   struct Iv1 {
       virtual void method0()=0;
   };

//And a class that implements the interface

   class Iv1Impl : public virtual Iv1 {
     virtual void method0(){}
   };

//Its not unreasonable to create a version 2 interface

   struct Iv2 : virtual Iv1 {
     virtual void method1()=0;};

//One might *try* to create an implementation class like this

   class Iv2Impl : public Iv2, Iv1Impl  {
     virtual void method1(){} // implement Iv2 method
   };

int main()
{
   Iv2Impl x;
}

regards
Andy Little
0
Reply andy199 (808) 8/6/2008 9:10:41 AM


You know.
That works.
Im sure I tried virtual inheritance before and it did not work.
Perhaps it collapsed because the compiler i tried with (VC++ 8) doesn't =
implement virtual base classes correctly, or perhaps there is something =
fundamental that goes wrong when one tries to mix in overridden and =
inherited methods.

"kwikius" <andy@servocomm.freeserve.co.uk> wrote in message =
news:48996a8a_1@mk-nntp-2.news.uk.tiscali.com...
> Chris Becke wrote:
>> Given an interface (in the c++ sense, nothing more than a struct =
containing pure virtual methods)
> <...>
>=20
>> That of course never never never works. Why not?=20
> > So what that Ive introduced another method0()=3D0 ?
> > Why can't it figure out it already has an implementation
> > for that method that I'd really like to allow it to use?
>=20
>=20
>   struct Iv1 {
>       virtual void method0()=3D0;
>   };
>=20
> //And a class that implements the interface
>=20
>   class Iv1Impl : public virtual Iv1 {
>     virtual void method0(){}
>   };
>=20
> //Its not unreasonable to create a version 2 interface
>=20
>   struct Iv2 : virtual Iv1 {
>     virtual void method1()=3D0;};
>=20
> //One might *try* to create an implementation class like this
>=20
>   class Iv2Impl : public Iv2, Iv1Impl  {
>     virtual void method1(){} // implement Iv2 method
>   };
>=20
> int main()
> {
>   Iv2Impl x;
> }
>=20
> regards
> Andy Little
0
Reply chris.becke (32) 8/6/2008 10:05:08 AM

Chris Becke wrote:
> You know.
> That works.
> Im sure I tried virtual inheritance before and it did not work.
> Perhaps it collapsed because the compiler i tried with (VC++ 8) doesn't implement virtual base classes correctly, or perhaps there is something fundamental that goes wrong when one tries to mix in overridden and inherited methods.

Well.. I  think I too know the "Doh..!" feeling... ;-)

Example was tested in VC8 FWIW, maybe your version was being grumpy 
today. Mine does that sometimes. Compilers... Love em hate but cant do 
without em.

regards
Andy Little
0
Reply andy199 (808) 8/6/2008 11:24:59 AM

kwikius wrote:
> Chris Becke wrote:
>> You know.
>> That works.
>> Im sure I tried virtual inheritance before and it did not work.
>> Perhaps it collapsed because the compiler i tried with (VC++ 8) 
>> doesn't implement virtual base classes correctly, or perhaps there is 
>> something fundamental that goes wrong when one tries to mix in 
>> overridden and inherited methods.
> 
> Well.. I  think I too know the "Doh..!" feeling... ;-)
> 
> Example was tested in VC8 FWIW, maybe your version was being grumpy 
> today. Mine does that sometimes. Compilers... Love em hate but cant do 
> without em.
> 
> regards
> Andy Little

Sorry to piggy-back on this thread, but it reminded me of a related 
interfaces problem I had a while back actually (I found a way to 
circumvent it, but it was a bit unsatisfactory).

Suppose you're implementing an inheritance hierarchy of Cartesian 
coordinate systems. You start off with interfaces:

ICoordSystem <-- IOrthogonalCoordSystem <-- IOrthonormalCoordSystem

You then want to implement each of these concretely:

ICoordSystem <-- CoordSystem
IOrthogonalCoordSystem <-- OrthogonalCoordSystem
IOrthonormalCoordSystem <-- OrthonormalCoordSystem

The only problem is, you want to reuse the logic in CoordSystem etc., so 
you also have this:

CoordSystem <-- OrthogonalCoordSystem <-- OrthonormalCoordSystem

At this point, I started having fun with my compiler. It was a while 
back now, so I can't quite remember what I tried any more before giving 
up and doing it another way, but how should I have done it please, out 
of interest?

I had come across virtual inheritance back then, but I clearly wasn't 
applying it as I should have been!

Cheers,
Stu
0
Reply sgolodetz (89) 8/7/2008 12:35:40 AM

Stuart Golodetz wrote:
> kwikius wrote:
>> Chris Becke wrote:
>>> You know.
>>> That works.
>>> Im sure I tried virtual inheritance before and it did not work.
>>> Perhaps it collapsed because the compiler i tried with (VC++ 8) 
>>> doesn't implement virtual base classes correctly, or perhaps there is 
>>> something fundamental that goes wrong when one tries to mix in 
>>> overridden and inherited methods.
>>
>> Well.. I  think I too know the "Doh..!" feeling... ;-)
>>
>> Example was tested in VC8 FWIW, maybe your version was being grumpy 
>> today. Mine does that sometimes. Compilers... Love em hate but cant do 
>> without em.
>>
>> regards
>> Andy Little
> 
> Sorry to piggy-back on this thread, but it reminded me of a related 
> interfaces problem I had a while back actually (I found a way to 
> circumvent it, but it was a bit unsatisfactory).
> 
> Suppose you're implementing an inheritance hierarchy of Cartesian 
> coordinate systems. You start off with interfaces:
> 
> ICoordSystem <-- IOrthogonalCoordSystem <-- IOrthonormalCoordSystem

(And when I say interfaces, I mean interface classes... I really am 
talking about C++ here :-) Though the code was being ported from Java, 
which is why I encountered this sort of problem.)

> You then want to implement each of these concretely:
> 
> ICoordSystem <-- CoordSystem
> IOrthogonalCoordSystem <-- OrthogonalCoordSystem
> IOrthonormalCoordSystem <-- OrthonormalCoordSystem
> 
> The only problem is, you want to reuse the logic in CoordSystem etc., so 
> you also have this:
> 
> CoordSystem <-- OrthogonalCoordSystem <-- OrthonormalCoordSystem
> 
> At this point, I started having fun with my compiler. It was a while 
> back now, so I can't quite remember what I tried any more before giving 
> up and doing it another way, but how should I have done it please, out 
> of interest?
> 
> I had come across virtual inheritance back then, but I clearly wasn't 
> applying it as I should have been!
> 
> Cheers,
> Stu
0
Reply sgolodetz (89) 8/7/2008 12:37:33 AM

Stuart Golodetz wrote:
> Stuart Golodetz wrote:
>> kwikius wrote:
>>> Chris Becke wrote:
>>>> You know.
>>>> That works.
>>>> Im sure I tried virtual inheritance before and it did not work.
>>>> Perhaps it collapsed because the compiler i tried with (VC++ 8) 
>>>> doesn't implement virtual base classes correctly, or perhaps there 
>>>> is something fundamental that goes wrong when one tries to mix in 
>>>> overridden and inherited methods.
>>>
>>> Well.. I  think I too know the "Doh..!" feeling... ;-)
>>>
>>> Example was tested in VC8 FWIW, maybe your version was being grumpy 
>>> today. Mine does that sometimes. Compilers... Love em hate but cant 
>>> do without em.
>>>
>>> regards
>>> Andy Little
>>
>> Sorry to piggy-back on this thread, but it reminded me of a related 
>> interfaces problem I had a while back actually (I found a way to 
>> circumvent it, but it was a bit unsatisfactory).
>>
>> Suppose you're implementing an inheritance hierarchy of Cartesian 
>> coordinate systems. You start off with interfaces:
>>
>> ICoordSystem <-- IOrthogonalCoordSystem <-- IOrthonormalCoordSystem
> 
> (And when I say interfaces, I mean interface classes... I really am 
> talking about C++ here :-) Though the code was being ported from Java, 
> which is why I encountered this sort of problem.)
> 
>> You then want to implement each of these concretely:
>>
>> ICoordSystem <-- CoordSystem
>> IOrthogonalCoordSystem <-- OrthogonalCoordSystem
>> IOrthonormalCoordSystem <-- OrthonormalCoordSystem
>>
>> The only problem is, you want to reuse the logic in CoordSystem etc., 
>> so you also have this:
>>
>> CoordSystem <-- OrthogonalCoordSystem <-- OrthonormalCoordSystem
>>
>> At this point, I started having fun with my compiler. It was a while 
>> back now, so I can't quite remember what I tried any more before 
>> giving up and doing it another way, but how should I have done it 
>> please, out of interest?
>>
>> I had come across virtual inheritance back then, but I clearly wasn't 
>> applying it as I should have been!
>>
>> Cheers,
>> Stu

Ok, I've recreated the problem:

struct I1
{
	virtual void f() = 0;
};

struct I2 : virtual I1
{
	virtual void g() = 0;
};

struct I3 : virtual I2
{
	virtual void h() = 0;
};

struct C1 : virtual I1
{
	void f()
	{
		std::cout << "f()" << std::endl;
	}
};

struct C2 : C1, virtual I2
{
	void g()
	{
		std::cout << "g()" << std::endl;
	}
};

struct C3 : C2, I3
{
	void h()
	{
		std::cout << "h()" << std::endl;
	}
};

When I do this, I get compiler warnings:

Warning	1	warning C4250: 'C2' : inherits 'C1::C1::f' via dominance	
Warning	2	warning C4250: 'C3' : inherits 'C1::C1::f' via dominance	
Warning	3	warning C4250: 'C3' : inherits 'C2::C2::g' via dominance	

Please can someone explain what's going on?

Cheers!
Stu
0
Reply sgolodetz (89) 8/7/2008 12:51:21 AM

On 2008-08-06 20:51:21 -0400, Stuart Golodetz 
<sgolodetz@NdOiSaPlA.pMiPpLeExA.ScEom> said:

> 
> Ok, I've recreated the problem:
> 
> struct I1
> {
> 	virtual void f() = 0;
> };
> 
> struct I2 : virtual I1
> {
> 	virtual void g() = 0;
> };
> 
> struct C1 : virtual I1
> {
> 	void f()
> 	{
> 		std::cout << "f()" << std::endl;
> 	}
> };
> 
> struct C2 : C1, virtual I2
> {
> 	void g()
> 	{
> 		std::cout << "g()" << std::endl;
> 	}
> };

[example simplified]

> 
> When I do this, I get compiler warnings:
> 
> Warning	1	warning C4250: 'C2' : inherits 'C1::C1::f' via dominance

[additional warnings elided]

> 	
> 
> Please can someone explain what's going on?
> 

The compiler is warning you that it's doing what the language 
definition says it should do, and that you might not be smart enough to 
understand what you've done. But you are: the code is exactly right.

Here's what's going on: C2 sees two definitons of f, one from C1 and 
one from i2 (which inherits it from i1). That would be ambiguous if it 
weren't for the dominance rule. Both C1 and i2 have i1 as a virtual 
base, so they both see the declaration of i1::f. C1 overrides i1::f, 
and i2 does not override it. C2 inherits from both, and the dominance 
rule says that a call to f on a C2 object is not ambiguous and calls 
C1::f. The definition of f that's nearer to C2 in the hierarchy 
dominates the one that's farther away. This rule only applies when all 
the inheritance paths lead to a common virtual base type that declares 
the function and only one of those paths has an overriding declaration. 
So if i2 also defined f, the call from C2 would be ambiguous. Got it? 
<g>

-- 
  Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The 
Standard C++ Library Extensions: a Tutorial and Reference 
(www.petebecker.com/tr1book)

0
Reply pete2666 (1733) 8/7/2008 1:30:54 AM

Stuart Golodetz wrote:

> Ok, I've recreated the problem:
>
> struct I1
> {
> 	virtual void f() = 0;
> };
>
> struct I2 : virtual I1
> {
> 	virtual void g() = 0;
> };
>
> struct I3 : virtual I2
> {
> 	virtual void h() = 0;
> };
>
> struct C1 : virtual I1
> {
> 	void f()
> 	{
> 		std::cout << "f()" << std::endl;
> 	}
> };
>
> struct C2 : C1, virtual I2
> {
> 	void g()
> 	{
> 		std::cout << "g()" << std::endl;
> 	}
> };
>
> struct C3 : C2, I3
> {
> 	void h()
> 	{
> 		std::cout << "h()" << std::endl;
> 	}
> };
>
> When I do this, I get compiler warnings:
>
> Warning	1	warning C4250: 'C2' : inherits 'C1::C1::f' via dominance	
> Warning	2	warning C4250: 'C3' : inherits 'C1::C1::f' via dominance	
> Warning	3	warning C4250: 'C3' : inherits 'C2::C2::g' via dominance	
>
> Please can someone explain what's going on?

C++ dominance: http://en.wikipedia.org/wiki/Dominance_(C%2B%2B)
--
Fran
0
Reply email3279 8/7/2008 1:39:41 AM

On Aug 7, 2:35 am, Stuart Golodetz
<sgolod...@NdOiSaPlA.pMiPpLeExA.ScEom> wrote:

> Sorry to piggy-back on this thread, but it reminded me of a
> related interfaces problem I had a while back actually (I
> found a way to circumvent it, but it was a bit
> unsatisfactory).

> Suppose you're implementing an inheritance hierarchy of
> Cartesian coordinate systems. You start off with interfaces:

> ICoordSystem <-- IOrthogonalCoordSystem <-- IOrthonormalCoordSystem

> You then want to implement each of these concretely:

> ICoordSystem <-- CoordSystem
> IOrthogonalCoordSystem <-- OrthogonalCoordSystem
> IOrthonormalCoordSystem <-- OrthonormalCoordSystem

> The only problem is, you want to reuse the logic in
> CoordSystem etc., so you also have this:

> CoordSystem <-- OrthogonalCoordSystem <-- OrthonormalCoordSystem

> At this point, I started having fun with my compiler. It was a
> while back now, so I can't quite remember what I tried any
> more before giving up and doing it another way, but how should
> I have done it please, out of interest?

This is a classical case; I've seen it both with interfaces (as
in your example), and with machine generated code instead of
your interfaces (the machine generated code provides a simple,
default implementation, which you can override).  I call it a
"ladder hierarchy", although I don't think I've seen this term
elsewhere:

              ICoordSystem <--- CoordSystem
                      ^              ^
                      |              |
                      |              |
    IOrthogonalCoordSystem <--- OrthogonalCoordSystem
                      ^              ^
                      |              |
                      |              |
   IOrthonormalCoordSystem <--- OrthonormalCoordSystem

(Except that in one case I encountered, it extended some six or
seven layers deep.)

Basically, anytime a class has more than one arrow leading into
it, inheritance must be virtual; the simplest solution is just
to make all heritage of the interface virtual:

    class ICoordSystem                          { /* ... */ } ;
    class IOrthogonalCoordSystem
        : public virtual ICoordSystem           { /* ... */ } ;
    class IOrthonormalCoordSystem
        : public virtual IOrthogonalCoordSystem { /* ... */ } ;
    class CoordSystem
        : public virtual ICoordSystem           { /* ... */ } ;
    class OrthogonalCoordSystem
        : public virtual IOrthogonalCoordSystem
        : private CoordSystem                   { /* ... */ } ;
    class OrthonormalCoordSystem
        : public virtual IOrthonormalCoordSystem
        : private OrthogonalCoordSystem         { /* ... */ } ;

(Note that the inheritance of the implementation is private.
This is usual as well, although not always necessary.)

--
James Kanze (GABI Software)             email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34
0
Reply james.kanze (9594) 8/7/2008 8:36:51 AM

On Aug 7, 3:30 am, Pete Becker <p...@versatilecoding.com> wrote:
> On 2008-08-06 20:51:21 -0400, Stuart Golodetz
> <sgolod...@NdOiSaPlA.pMiPpLeExA.ScEom> said:

    [...]
> > When I do this, I get compiler warnings:

> > Warning    1       warning C4250: 'C2' : inherits 'C1::C1::f' via domin=
ance

> > Please can someone explain what's going on?

> The compiler is warning you that it's doing what the language
> definition says it should do, and that you might not be smart
> enough to understand what you've done. But you are: the code
> is exactly right.

But isn't this a stupid warning?  The formal specification of
dominance might be a little complicated (but only a very
little), but in practice, it always does exactly what one would
intuitively expect and want.  While I can see the compiler
warning when the standard requires it to do something you likely
don't want, or even when it isn't clear what you might want
(what happens when comparing signed and unsigned, for example),
this looks to me very close to warning that the generated code
will do an addition, because you used the + operator.

--
James Kanze (GABI Software)             email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34
0
Reply james.kanze (9594) 8/7/2008 8:41:42 AM

On 2008-08-07 04:41:42 -0400, James Kanze <james.kanze@gmail.com> said:

> On Aug 7, 3:30 am, Pete Becker <p...@versatilecoding.com> wrote:
>> On 2008-08-06 20:51:21 -0400, Stuart Golodetz
>> <sgolod...@NdOiSaPlA.pMiPpLeExA.ScEom> said:
> 
>     [...]
>>> When I do this, I get compiler warnings:
> 
>>> Warning    1       warning C4250: 'C2' : inherits 'C1::C1::f' via domin
> ance
> 
>>> Please can someone explain what's going on?
> 
>> The compiler is warning you that it's doing what the language
>> definition says it should do, and that you might not be smart
>> enough to understand what you've done. But you are: the code
>> is exactly right.
> 
> But isn't this a stupid warning?

Yes. My original draft started out "Stupid compiler warnings."

>   The formal specification of
> dominance might be a little complicated (but only a very
> little), but in practice, it always does exactly what one would
> intuitively expect and want.  While I can see the compiler
> warning when the standard requires it to do something you likely
> don't want, or even when it isn't clear what you might want
> (what happens when comparing signed and unsigned, for example),
> this looks to me very close to warning that the generated code
> will do an addition, because you used the + operator.

Maybe it means "we worked really hard to understand and implement 
dominance, so we want to make sure you notice it".

-- 
  Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The 
Standard C++ Library Extensions: a Tutorial and Reference 
(www.petebecker.com/tr1book)

0
Reply pete2666 (1733) 8/7/2008 10:14:05 AM

Pete Becker <pete@versatilecoding.com> wrote in
news:2008080706140516807-pete@versatilecodingcom: 

> 
> Maybe it means "we worked really hard to understand and implement 
> dominance, so we want to make sure you notice it".
> 

It probably means that they have gotten a lot of bug reports about this 
feature and they are doing a CYA manuver.  :)

joe
0
Reply jgreer5024 (238) 8/7/2008 1:02:01 PM

>   struct Iv2 : virtual Iv1 {
>     virtual void method1()=3D0;};

Actually, this is where the house of cards falls down :- In a framework =
that uses interfaces to facilitate communication between precompiled =
modules potentially written in different languages you cannot ever use =
'virtual' to extend an interface. The final resulting vtables MUST be =
contiguous in memory and using virtual will fragment them. :-(

"kwikius" <andy@servocomm.freeserve.co.uk> wrote in message =
news:48996a8a_1@mk-nntp-2.news.uk.tiscali.com...
> Chris Becke wrote:
>> Given an interface (in the c++ sense, nothing more than a struct =
containing pure virtual methods)
> <...>
>=20
>> That of course never never never works. Why not?=20
> > So what that Ive introduced another method0()=3D0 ?
> > Why can't it figure out it already has an implementation
> > for that method that I'd really like to allow it to use?
>=20
>=20
>   struct Iv1 {
>       virtual void method0()=3D0;
>   };
>=20
> //And a class that implements the interface
>=20
>   class Iv1Impl : public virtual Iv1 {
>     virtual void method0(){}
>   };
>=20
> //Its not unreasonable to create a version 2 interface
>=20
>   struct Iv2 : virtual Iv1 {
>     virtual void method1()=3D0;};
>=20
> //One might *try* to create an implementation class like this
>=20
>   class Iv2Impl : public Iv2, Iv1Impl  {
>     virtual void method1(){} // implement Iv2 method
>   };
>=20
> int main()
> {
>   Iv2Impl x;
> }
>=20
> regards
> Andy Little
0
Reply chris.becke (32) 8/7/2008 2:09:29 PM

On 2008-08-07 10:09:29 -0400, "Chris Becke" <chris.becke@gmail.com> said:

>> struct Iv2 : virtual Iv1 {
>> virtual void method1()=0;};
> 
> Actually, this is where the house of cards falls down :- In a framework
> that uses interfaces to facilitate communication between precompiled
> modules potentially written in different languages you cannot ever use
> 'virtual' to extend an interface. The final resulting vtables MUST be
> contiguous in memory and using virtual will fragment them. :-(

Huh? If you don't use virtuals you won't get vtables. Unless by 
"vtable" you mean some non-C++ interoperability mechanism, in which 
case, it's not particularly pertinent here.

-- 
  Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The 
Standard C++ Library Extensions: a Tutorial and Reference 
(www.petebecker.com/tr1book)

0
Reply pete2666 (1733) 8/7/2008 2:51:20 PM

On Aug 7, 3:09=A0pm, "Chris Becke" <chris.be...@gmail.com> wrote:
> > =A0 struct Iv2 : virtual Iv1 {
> > =A0 =A0 virtual void method1()=3D0;};
>
> Actually, this is where the house of cards
> falls down

FWIW the main problem with the base derived hierarchy scheme is that
if you have more than a couple of levels, then each layer gets a
vtable and most of the vtable isnt used so you have a lot of dead
space in a class.

Sometimes its nice just to have a function pointer in the base class
that you just replace in the derived class. You can use CRTP to
achieve this and get some support from the language. In this case you
need a non template base class with virtual functions and then a
templated base class which is the CRTP base class derived from base
which provides an impl f the functions. You then implement anything
you want to overload in the derived which is passed as a parameter to
the templated base,
The templated base reaches down into the derived to see if its
functions have been overloaded. The technicalities are somewhat
slightly more complex in practise but you can end up with a much
lighter and faster hierarchy.

regards
Andy Little
0
Reply andy199 (808) 8/7/2008 3:55:37 PM

Pete Becker wrote:
> On 2008-08-06 20:51:21 -0400, Stuart Golodetz 
> <sgolodetz@NdOiSaPlA.pMiPpLeExA.ScEom> said:
> 
>>
>> Ok, I've recreated the problem:
>>
>> struct I1
>> {
>>     virtual void f() = 0;
>> };
>>
>> struct I2 : virtual I1
>> {
>>     virtual void g() = 0;
>> };
>>
>> struct C1 : virtual I1
>> {
>>     void f()
>>     {
>>         std::cout << "f()" << std::endl;
>>     }
>> };
>>
>> struct C2 : C1, virtual I2
>> {
>>     void g()
>>     {
>>         std::cout << "g()" << std::endl;
>>     }
>> };
> 
> [example simplified]
> 
>>
>> When I do this, I get compiler warnings:
>>
>> Warning    1    warning C4250: 'C2' : inherits 'C1::C1::f' via dominance
> 
> [additional warnings elided]
> 
>>     
>>
>> Please can someone explain what's going on?
>>
> 
> The compiler is warning you that it's doing what the language definition 
> says it should do, and that you might not be smart enough to understand 
> what you've done. But you are: the code is exactly right.
> 
> Here's what's going on: C2 sees two definitons of f, one from C1 and one 
> from i2 (which inherits it from i1). That would be ambiguous if it 
> weren't for the dominance rule. Both C1 and i2 have i1 as a virtual 
> base, so they both see the declaration of i1::f. C1 overrides i1::f, and 
> i2 does not override it. C2 inherits from both, and the dominance rule 
> says that a call to f on a C2 object is not ambiguous and calls C1::f. 
> The definition of f that's nearer to C2 in the hierarchy dominates the 
> one that's farther away. This rule only applies when all the inheritance 
> paths lead to a common virtual base type that declares the function and 
> only one of those paths has an overriding declaration. So if i2 also 
> defined f, the call from C2 would be ambiguous. Got it? <g>

Think so - thanks (it's a little bit intricate) :-)
0
Reply sgolodetz (89) 8/7/2008 5:17:11 PM

Pete Becker wrote:
> On 2008-08-07 04:41:42 -0400, James Kanze <james.kanze@gmail.com> said:
> 
>> On Aug 7, 3:30 am, Pete Becker <p...@versatilecoding.com> wrote:
>>> On 2008-08-06 20:51:21 -0400, Stuart Golodetz
>>> <sgolod...@NdOiSaPlA.pMiPpLeExA.ScEom> said:
>>
>>     [...]
>>>> When I do this, I get compiler warnings:
>>
>>>> Warning    1       warning C4250: 'C2' : inherits 'C1::C1::f' via domin
>> ance
>>
>>>> Please can someone explain what's going on?
>>
>>> The compiler is warning you that it's doing what the language
>>> definition says it should do, and that you might not be smart
>>> enough to understand what you've done. But you are: the code
>>> is exactly right.
>>
>> But isn't this a stupid warning?
> 
> Yes. My original draft started out "Stupid compiler warnings."
> 
>>   The formal specification of
>> dominance might be a little complicated (but only a very
>> little), but in practice, it always does exactly what one would
>> intuitively expect and want.  While I can see the compiler
>> warning when the standard requires it to do something you likely
>> don't want, or even when it isn't clear what you might want
>> (what happens when comparing signed and unsigned, for example),
>> this looks to me very close to warning that the generated code
>> will do an addition, because you used the + operator.
> 
> Maybe it means "we worked really hard to understand and implement 
> dominance, so we want to make sure you notice it".

And to think - if they hadn't warned about it, I'd never have given it a 
second thought or understood it properly :-) I'm kind of oddly grateful. 
Though I think I might be disabling that particular warning in future...
0
Reply sgolodetz (89) 8/7/2008 5:23:44 PM

On 2008-08-07 13:17:11 -0400, Stuart Golodetz 
<sgolodetz@NdOiSaPlA.pMiPpLeExA.ScEom> said:

> 
> Think so - thanks (it's a little bit intricate) :-)

Drawing a picture can help.

-- 
  Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The 
Standard C++ Library Extensions: a Tutorial and Reference 
(www.petebecker.com/tr1book)

0
Reply pete2666 (1733) 8/7/2008 5:34:14 PM

James Kanze wrote:
> On Aug 7, 2:35 am, Stuart Golodetz
> <sgolod...@NdOiSaPlA.pMiPpLeExA.ScEom> wrote:
> 
>> Sorry to piggy-back on this thread, but it reminded me of a
>> related interfaces problem I had a while back actually (I
>> found a way to circumvent it, but it was a bit
>> unsatisfactory).
> 
>> Suppose you're implementing an inheritance hierarchy of
>> Cartesian coordinate systems. You start off with interfaces:
> 
>> ICoordSystem <-- IOrthogonalCoordSystem <-- IOrthonormalCoordSystem
> 
>> You then want to implement each of these concretely:
> 
>> ICoordSystem <-- CoordSystem
>> IOrthogonalCoordSystem <-- OrthogonalCoordSystem
>> IOrthonormalCoordSystem <-- OrthonormalCoordSystem
> 
>> The only problem is, you want to reuse the logic in
>> CoordSystem etc., so you also have this:
> 
>> CoordSystem <-- OrthogonalCoordSystem <-- OrthonormalCoordSystem
> 
>> At this point, I started having fun with my compiler. It was a
>> while back now, so I can't quite remember what I tried any
>> more before giving up and doing it another way, but how should
>> I have done it please, out of interest?
> 
> This is a classical case; I've seen it both with interfaces (as
> in your example), and with machine generated code instead of
> your interfaces (the machine generated code provides a simple,
> default implementation, which you can override).  I call it a
> "ladder hierarchy", although I don't think I've seen this term
> elsewhere:
> 
>               ICoordSystem <--- CoordSystem
>                       ^              ^
>                       |              |
>                       |              |
>     IOrthogonalCoordSystem <--- OrthogonalCoordSystem
>                       ^              ^
>                       |              |
>                       |              |
>    IOrthonormalCoordSystem <--- OrthonormalCoordSystem
> 
> (Except that in one case I encountered, it extended some six or
> seven layers deep.)
> 
> Basically, anytime a class has more than one arrow leading into
> it, inheritance must be virtual; the simplest solution is just
> to make all heritage of the interface virtual:
> 
>     class ICoordSystem                          { /* ... */ } ;
>     class IOrthogonalCoordSystem
>         : public virtual ICoordSystem           { /* ... */ } ;
>     class IOrthonormalCoordSystem
>         : public virtual IOrthogonalCoordSystem { /* ... */ } ;
>     class CoordSystem
>         : public virtual ICoordSystem           { /* ... */ } ;
>     class OrthogonalCoordSystem
>         : public virtual IOrthogonalCoordSystem
>         : private CoordSystem                   { /* ... */ } ;
>     class OrthonormalCoordSystem
>         : public virtual IOrthonormalCoordSystem
>         : private OrthogonalCoordSystem         { /* ... */ } ;
> 
> (Note that the inheritance of the implementation is private.
> This is usual as well, although not always necessary.)

Thanks :-) It's nice to see a valid use of private inheritance as well - 
I've never really found a use for it in the past (I just public 
inheritance if LSP is satisfied and it's appropriate, and avoid 
inheritance entirely otherwise).
0
Reply sgolodetz (89) 8/7/2008 5:38:13 PM

Pete Becker wrote:
> On 2008-08-07 13:17:11 -0400, Stuart Golodetz 
> <sgolodetz@NdOiSaPlA.pMiPpLeExA.ScEom> said:
> 
>>
>> Think so - thanks (it's a little bit intricate) :-)
> 
> Drawing a picture can help.

I did :-)
0
Reply sgolodetz (89) 8/7/2008 5:40:06 PM

In article <2008080710512016807-pete@versatilecodingcom>, 
pete@versatilecoding.com says...
> On 2008-08-07 10:09:29 -0400, "Chris Becke" <chris.becke@gmail.com> said:
> 
> >> struct Iv2 : virtual Iv1 {
> >> virtual void method1()=0;};
> > 
> > Actually, this is where the house of cards falls down :- In a framework
> > that uses interfaces to facilitate communication between precompiled
> > modules potentially written in different languages you cannot ever use
> > 'virtual' to extend an interface. The final resulting vtables MUST be
> > contiguous in memory and using virtual will fragment them. :-(
> 
> Huh? If you don't use virtuals you won't get vtables. Unless by 
> "vtable" you mean some non-C++ interoperability mechanism, in which 
> case, it's not particularly pertinent here.

I think he meant the "virtual" to refer to virtual inheritance, not 
virtual functions. 

-- 
    Later,
    Jerry.

The universe is a figment of its own imagination.
0
Reply jcoffin (2240) 8/8/2008 4:47:05 AM

On 2008-08-08 00:47:05 -0400, Jerry Coffin <jcoffin@taeus.com> said:

> In article <2008080710512016807-pete@versatilecodingcom>,
> pete@versatilecoding.com says...
>> On 2008-08-07 10:09:29 -0400, "Chris Becke" <chris.becke@gmail.com> said:
>> 
>>>> struct Iv2 : virtual Iv1 {
>>>> virtual void method1()=0;};
>>> 
>>> Actually, this is where the house of cards falls down :- In a framework
>>> that uses interfaces to facilitate communication between precompiled
>>> modules potentially written in different languages you cannot ever use
>>> 'virtual' to extend an interface. The final resulting vtables MUST be
>>> contiguous in memory and using virtual will fragment them. :-(
>> 
>> Huh? If you don't use virtuals you won't get vtables. Unless by
>> "vtable" you mean some non-C++ interoperability mechanism, in which
>> case, it's not particularly pertinent here.
> 
> I think he meant the "virtual" to refer to virtual inheritance, not
> virtual functions.

I still have no idea what it means. The compiler lays out vtables, and 
makes them work. Doesn't matter if they're "fragmented" (if that's what 
happens, I haven't seen it) so long as the compiler knows how to deal 
with them.

-- 
  Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The 
Standard C++ Library Extensions: a Tutorial and Reference 
(www.petebecker.com/tr1book)

0
Reply pete2666 (1733) 8/8/2008 11:32:28 AM

On Aug 8, 1:32 pm, Pete Becker <p...@versatilecoding.com> wrote:
> On 2008-08-08 00:47:05 -0400, Jerry Coffin <jcof...@taeus.com> said:
> > In article <2008080710512016807-pete@versatilecodingcom>,
> > p...@versatilecoding.com says...
> >> On 2008-08-07 10:09:29 -0400, "Chris Becke" <chris.be...@gmail.com> sa=
id:

> >>>> struct Iv2 : virtual Iv1 {
> >>>> virtual void method1()=3D0;};

> >>> Actually, this is where the house of cards falls down :-
> >>> In a framework that uses interfaces to facilitate
> >>> communication between precompiled modules potentially
> >>> written in different languages you cannot ever use
> >>> 'virtual' to extend an interface. The final resulting
> >>> vtables MUST be contiguous in memory and using virtual
> >>> will fragment them. :-(

> >> Huh? If you don't use virtuals you won't get vtables.
> >> Unless by "vtable" you mean some non-C++ interoperability
> >> mechanism, in which case, it's not particularly pertinent
> >> here.

> > I think he meant the "virtual" to refer to virtual
> > inheritance, not virtual functions.

> I still have no idea what it means. The compiler lays out
> vtables, and makes them work. Doesn't matter if they're
> "fragmented" (if that's what happens, I haven't seen it) so
> long as the compiler knows how to deal with them.

Well, Chris Becke did mention linking with other languages.  If
that's going to work, the compiler has to lay out everything
(including vtables) in a compatible format (and the other
language has to support whatever it is you're trying to
do---virtual functions don't work too well in an `extern "C"',
for example).

It's all very implementation defined.  Generally, either the
compilers of both languages concure (and you use `extern
"Whatever"' in C++, to tell the compiler that it has to follow
the conventions of the other language), or you have to use some
external tool such as Corba.  In a lot of cases, the only thing
different languages will support is the C API, which means that
anything passed between them must be C compatible.  If the other
language does provide the equivalent of an `extern "C++"', it's
possible that it still imposes some restrictions (no templates
would seem likely, but no virtual inheritance, or even no
multiple inheritance, is also a distinct possibility).  In some
cases (Java's JNI, for example), the other language defines what
the interface will look like, and your C++ code has to conform;
I'm pretty sure that JNI supports virtual functions in C++, but
I'm also pretty sure that either it doesn't allow multiple
inheritance, or it imposes severe restrictions on it.  And
external tools will impose their own constraints.

None of which, of course, is really relevant here.  If you're
using tools which impose artificial constraints on the language,
that's not really C++ (and you should seriously think about
changing the tool set).

--
James Kanze (GABI Software)             email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34
0
Reply james.kanze (9594) 8/8/2008 12:11:21 PM

"James Kanze" <james.kanze@gmail.com> wrote in message =
news:dc62d088-11d9-4bd7-bf22-8f96a02c5374@2g2000hsn.googlegroups.com...
>Well, Chris Becke did mention linking with other languages.  If
>that's going to work, the compiler has to lay out everything
>(including vtables) in a compatible format (and the other
>language has to support whatever it is you're trying to
>do---virtual functions don't work too well in an `extern "C"',
>for example).

Binary compatibility between "modules" that may have been compiled in =
different languages has always been a case of co-ercing the compliant =
compilers on the platform in question to produce compatible code.

So, Microsoft defines a technology called COM that relies on a table of =
function pointers to be passed between modules. These tables can be =
defined manually in C, as struct containing function pointers, each =
function takes a pointer to an object as its first parameter. In c++ its =
convenient to declare these interfaces by creating a struct / public =
class containing pure virtual functions - and later on implement the =
virtual functions in a derived class.

However, as it turns out, c++ makes these things horrible to extend, or =
have inheritable implement for.

Pointing out that c++ leaves the layout of vtables to implementations =
is, well, redundant as that was the point of my original post: I wish =
that c++ did interfaces better.



It's all very implementation defined.  Generally, either the
compilers of both languages concure (and you use `extern
"Whatever"' in C++, to tell the compiler that it has to follow
the conventions of the other language), or you have to use some
external tool such as Corba.  In a lot of cases, the only thing
different languages will support is the C API, which means that
anything passed between them must be C compatible.  If the other
language does provide the equivalent of an `extern "C++"', it's
possible that it still imposes some restrictions (no templates
would seem likely, but no virtual inheritance, or even no
multiple inheritance, is also a distinct possibility).  In some
cases (Java's JNI, for example), the other language defines what
the interface will look like, and your C++ code has to conform;
I'm pretty sure that JNI supports virtual functions in C++, but
I'm also pretty sure that either it doesn't allow multiple
inheritance, or it imposes severe restrictions on it.  And
external tools will impose their own constraints.

None of which, of course, is really relevant here.  If you're
using tools which impose artificial constraints on the language,
that's not really C++ (and you should seriously think about
changing the tool set).

--
James Kanze (GABI Software)             email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34
0
Reply chris.becke (32) 8/12/2008 10:10:07 AM

On Aug 12, 12:10 pm, "Chris Becke" <chris.be...@gmail.com> wrote:
> "James Kanze" <james.ka...@gmail.com> wrote in messagenews:dc62d088-11d9-=
4bd7-bf22-8f96a02c5374@2g2000hsn.googlegroups.com...
> >Well, Chris Becke did mention linking with other languages.  If
> >that's going to work, the compiler has to lay out everything
> >(including vtables) in a compatible format (and the other
> >language has to support whatever it is you're trying to
> >do---virtual functions don't work too well in an `extern "C"',
> >for example).

> Binary compatibility between "modules" that may have been
> compiled in different languages has always been a case of
> co-ercing the compliant compilers on the platform in question
> to produce compatible code.

> So, Microsoft defines a technology called COM that relies on a
> table of function pointers to be passed between modules. These
> tables can be defined manually in C, as struct containing
> function pointers, each function takes a pointer to an object
> as its first parameter. In c++ its convenient to declare these
> interfaces by creating a struct / public class containing pure
> virtual functions - and later on implement the virtual
> functions in a derived class.

In which case, you're talking about problems with COM, and not
with C++.

> However, as it turns out, c++ makes these things horrible to
> extend, or have inheritable implement for.

> Pointing out that c++ leaves the layout of vtables to
> implementations is, well, redundant as that was the point of
> my original post: I wish that c++ did interfaces better.

But you didn't define better.  And the problems you describe
sound more like problems specific to COM (which isn't C++) or to
Microsoft's implementation, or to both.  At least, I've never
been affected by them (but of course, for interlanguage
communication, I use Corba, since COM isn't really very well
supported once you leave the Windows world).

--
James Kanze (GABI Software)             email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34
0
Reply james.kanze (9594) 8/12/2008 2:41:00 PM

"James Kanze" <james.kanze@gmail.com> wrote in message =
news:6fef25cf-b57f-4643-a672-43b4f7192169@59g2000hsb.googlegroups.com...
>But you didn't define better.  And the problems you describe
>sound more like problems specific to COM (which isn't C++) or to
>Microsoft's implementation, or to both.  At least, I've never
>been affected by them (but of course, for interlanguage
>communication, I use Corba, since COM isn't really very well
>supported once you leave the Windows world).

Leaving the actual layout of vtables up to the compiler vendors - I mean =
a compiler vendor targetting a COM compatible platform would, perforce, =
ensure that basic vtables are layed out in the expected / required =
binary fashion...

I would wish that c++ made my life easier by providing some way for me =
to say, "Yes, I know Ive inherited from that struct of virtual methods =
several times, but I have also inherited an implementation - it might be =
further away inheritance wise, but use it anyway". Which is to say, I =
want a non clumsy way to do the following
1. declare an interface (IA), declare an interface that extends the =
first interface (IB) - and to be COM complaint the IB -> IA relationship =
cannot be virtual.
2. And then declare a class A that implements IA, as well as=20
3. a class B, that inherits the IA implementation as well as =
implementing IB. Step 3 is the problem.

  struct IA {
    virtual method1()=3D0;=20
  };
  struct IB : IA {
    virtual method2()=3D0;
  };
  class A: IA {
    method1(){}
  };
  class B:A,IB {
    method2(){}
  };

0
Reply chris.becke (32) 8/12/2008 3:00:18 PM

26 Replies
25 Views

(page loaded in 0.228 seconds)


Reply: