Account Options

  1. Sign in
The old Google Groups will be going away soon, but your browser is incompatible with the new version.
Google Groups Home
« Groups Home
Multiple Inheritance vs. Interface
There are currently too many topics in this group that display first. To make this topic appear first, remove this option from another topic.
There was an error processing your request. Please try again.
flag
  Messages 1 - 25 of 30 - Collapse all  -  Translate all to Translated (View all originals)   Newer >
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
lieve again  
View profile  
 More options Sep 20 2012, 8:59 am
Newsgroups: comp.lang.c++
From: lieve again <hablando...@gmail.com>
Date: Thu, 20 Sep 2012 05:59:03 -0700 (PDT)
Local: Thurs, Sep 20 2012 8:59 am
Subject: Multiple Inheritance vs. Interface
Hi!
I have a question regarding the implementation of the multiple
inheritance in C++.
As far as I know, the implementation problem of multiple inheritance
(one of them) in every programming language is the need for an extra
pointer for each new inherited class.
For example:
class Base1{
 // hidden Base1_vtr = &Base1_vtbl[0]
 virtual void func();
 virtual void func2();
 virtual void func3();
 };
 class Base2{
  // hidden Base2_vtr = &Base2_vtbl[0]
 virtual base2func();
 virtual base2func2();
 };

 class NormalInheritance : public Base1 {
 // hidden Base1_vtr = &Base1_vtbl[0]
 virtual void anotherFunc();
 };

 class MultipleInheritance : public Base1, Base2 {
 // hidden Base1_vtr = &Base1_vtbl[0]
 // hidden Base2_vtr = &Base2_vtbl[0]
virtual void anotherFunc();

};

So, sizeof(Base1) == sizeof(Base2) == sizeof(NormalInheritance) == 4
bytes (only one virtual pointer)
but sizeof(MultipleInheritance) == 8 bytes
if class MultipleInheritance would inherit from another Base3, the
size would be 12 bytes and so on.

So with multiple inheritance we ends with big classes because of the
need of extra virtual pointers,
to avoid that, almost every language doesn't implement multiple
inheritance but Interfaces, where one
can only inherit more than one class but being that classes abstract
or pure virtual, like:

class Base1{
 virtual void func() = 0;
 virtual void func3();
 };

 class MixedClass : public NormalClass implements Base1, Base2,
Base...

 My question is: Don't we have the same implementation problem as in C+
+? Because even being these classes
 abstract, they need a virtual pointer. Why do they impose that rule
in languages like C#, Java, D...?
 Someone know the reason?


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Öö Tiib  
View profile  
 More options Sep 21 2012, 10:16 pm
Newsgroups: comp.lang.c++
From: Öö Tiib <oot...@hot.ee>
Date: Fri, 21 Sep 2012 19:16:39 -0700 (PDT)
Local: Fri, Sep 21 2012 10:16 pm
Subject: Re: Multiple Inheritance vs. Interface

On Thursday, September 20, 2012 3:59:04 PM UTC+3, lieve again wrote:
> So, sizeof(Base1) == sizeof(Base2) == sizeof(NormalInheritance) == 4
> bytes (only one virtual pointer)
> but sizeof(MultipleInheritance) == 8 bytes
> if class MultipleInheritance would inherit from another Base3, the
> size would be 12 bytes and so on.

It depends on implementation. "virtual" is just a keyword in C++ language.
While vtable is one usual way to implement it ... and multiple vtables is one
way to implement multiple inheritance ... it is NOWHERE written that you have
to do so. The programs written in C# are about 5 times less efficient than same
in C++ and so you could draw false conclusions that multiple inheritance is 5
times more efficient than no multiple inheritance.

> So with multiple inheritance we ends with big classes because of the
> need of extra virtual pointers,

You are mixing classes with objects here. You should stop. Just take few steps
back and learn basics. What is class what is object and what is the difference.

 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Paavo Helde  
View profile  
 More options Sep 22 2012, 2:42 am
Newsgroups: comp.lang.c++
From: Paavo Helde <myfirstn...@osa.pri.ee>
Date: Sat, 22 Sep 2012 01:42:43 -0500
Local: Sat, Sep 22 2012 2:42 am
Subject: Re: Multiple Inheritance vs. Interface
lieve again <hablando...@gmail.com> wrote in news:bc8e026d-f13e-4b87-
8fe1-84bedf0e6...@o30g2000vbu.googlegroups.com:

> Hi!
> I have a question regarding the implementation of the multiple
> inheritance in C++.
> As far as I know, the implementation problem of multiple inheritance
> (one of them) in every programming language is the need for an extra
> pointer for each new inherited class.

The biggest problems with multiple inheritance are conceptual. One very
quickly stomps on the diamond problem; C++ kind of solves this with the
virtual inheritance feature, but this solution is quite complex, reduces
performance a bit, is not very clean (middle classes not really self-
contained as a tradeoff to keep the performance loss localized) and can
easily confuse the programmer. In C++ this is OK as the rest of the
language is also very complex and tricky, but it does not really fit into
mindset of "easy and safe" languages like Java.

The diamond problem is amplified by a common OO approach where everything
is derived from the single "Object" base class. Now whenever one uses
multiple inheritance, the diamond problem pops up. To solve it one should
always inherit virtually from Object, meaning the loss of performance all
over the place, most probably even in programs which never use multiple
inheritance at all.

C++ neatly side-steps this issue by not mandating such common base class
and using templates instead to achieve similar functionality.

Java neatly side-steps this issue by banning multiple inheritance. To
each his own.

Cheers
Paavo


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
lieve again  
View profile  
 More options Sep 22 2012, 7:00 am
Newsgroups: comp.lang.c++
From: lieve again <hablando...@gmail.com>
Date: Sat, 22 Sep 2012 04:00:05 -0700 (PDT)
Local: Sat, Sep 22 2012 7:00 am
Subject: Re: Multiple Inheritance vs. Interface

> > So with multiple inheritance we ends with big classes because of the
> > need of extra virtual pointers,

> You are mixing classes with objects here. You should stop. Just take few steps
> back and learn basics. What is class what is object and what is the difference.

First thank you for the answers.

I didn't mean classes but object or the instances of the class:

Base1 base1;                  // sizeof(base1) == 4 bytes
MultipleInheritance multiple; // sizeof(multiple) == 8 bytes

I meant, supposing the vtable implementation is being used (by far
the
most used), we are adding to any object or instance of the class an
extra pointer.

About the diamond problem, still complex, can be solved with the
virtual inheritance and the scope resolution operator:

multiple->Base1::func();
multiple->Base2::func();

So, my question was what is the benefit or making that classes
abstract or pure virtual in languages like C#, D...
We have the same extra pointers in the object, haven't we?

Cheers, lieve


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Öö Tiib  
View profile  
 More options Sep 22 2012, 7:11 am
Newsgroups: comp.lang.c++
From: Öö Tiib <oot...@hot.ee>
Date: Sat, 22 Sep 2012 04:11:24 -0700 (PDT)
Local: Sat, Sep 22 2012 7:11 am
Subject: Re: Multiple Inheritance vs. Interface

Benefit is that you can derive a class only from one class that actually
implements something. So one of ways to achieve serious complexity (make deep
and wide inheritance hierarchies) is removed from your toolset. As result you
can not burn the brains of novice maintainer who has to work on your code with
the class hierarchy.

Most modern programming languages try to be simple to use and to understand.
C++ trys to be efficient and feature-rich instead. C++ can be also simple
to use and to understand but then you have to agree with your team that
you follow certain idioms strictly and avoid using lot of things that are
legal C++ by standard.


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Richard Damon  
View profile  
 More options Sep 22 2012, 2:10 pm
Newsgroups: comp.lang.c++
From: Richard Damon <news.x.richardda...@xoxy.net>
Date: Sat, 22 Sep 2012 14:10:06 -0400
Local: Sat, Sep 22 2012 2:10 pm
Subject: Re: Multiple Inheritance vs. Interface
On 9/20/12 8:59 AM, lieve again wrote:

> So, sizeof(Base1) == sizeof(Base2) == sizeof(NormalInheritance) == 4
> bytes (only one virtual pointer)
> but sizeof(MultipleInheritance) == 8 bytes
> if class MultipleInheritance would inherit from another Base3, the
> size would be 12 bytes and so on.

And this should be expected as we would expect
sizeof(MultipleInheritance) = sizeof(Base1) + sizeof(Base2) +
    sizeof(stuff added in MultipleInheritance)

as we would normally expect that each of the base classes be fully
represented within the derived class so it is easy to treat the derived
class as if it was any of its base classes. (There is an exception for
empty base classes which must have a sizeof > 0 as a class by itself,
but might not take any extra room when derived from.)

When inheriting from Interfaces, the difference is that the Interface
never needs to exist as a discrete object, so there isn't a need to save
a vtable pointer for each Interface. The Interface routine likely need a
pointer into the base class vtable to the vtable for that interface, but
that should be computable from the object normal vtable pointer.

The main reason many languages don't implement multiple inheritance (but
maybe Interfaces) is NOT object size, but language a program complexity.


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Stuart  
View profile  
 More options Sep 23 2012, 10:32 am
Newsgroups: comp.lang.c++
From: Stuart <DerTop...@web.de>
Date: Sun, 23 Sep 2012 16:32:24 +0200
Local: Sun, Sep 23 2012 10:32 am
Subject: Re: Multiple Inheritance vs. Interface
On 9/20/12 "lieve again" wrote:

That's right.

> So with multiple inheritance we ends with big classes because of the
> need of extra virtual pointers,
> to avoid that, almost every language doesn't implement multiple
> inheritance but Interfaces,

I don't think that this is the reason why the implementors of other
languages chose a single-inheritance approach. In my opinion it is the
complexity of a multiple-inheritance language that puts off a lot of
low-end programmers, but these programmers are the bulk of the
industry's employees.

> where one
> can only inherit more than one class but being that classes abstract
> or pure virtual, like:

> class Base1{
>   virtual void func() = 0;
>   virtual void func3();
>   };

>   class MixedClass : public NormalClass implements Base1, Base2,
> Base...

>   My question is: Don't we have the same implementation problem as in C+
> +?

Yes, single inheritance languages that are implemented using vtables
still suffer from this problem.

 > Because even being these classes

>   abstract, they need a virtual pointer. Why do they impose that rule
> in languages like C#, Java, D...?
>   Someone know the reason?

We can only guess. Unfortunately, there is no single person that has
perceived the Java programming language, so we cannot ask somebody (in
contrast to Mr. Stroustrup, who, although rarely, can be seen in this
newsgroup).

Regards,
Stuart


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
lieve again  
View profile  
 More options Sep 23 2012, 4:40 pm
Newsgroups: comp.lang.c++
From: lieve again <hablando...@gmail.com>
Date: Sun, 23 Sep 2012 13:40:42 -0700 (PDT)
Local: Sun, Sep 23 2012 4:40 pm
Subject: Re: Multiple Inheritance vs. Interface
On 23 sep, 16:32, Stuart <DerTop...@web.de> wrote:

Ok, so the main reason for not implementing multiple inheritance
(without workarounds) is the complexity added to the programmers
(learning curve) and to the compiler developers (diamond
problem, ...). I thought maybe making the interfaces pure virtual,
there was a way to avoid the extra vpointers and I wanted to know how.
Then if I start adding pure virtual classes to impose the derived
classes with some kind of features like:
class Derived : implements Readable, Writeable, Comparable,
Convertible ...
regardless of the programming language, we are ending with instances
of the derived classes having 20 bytes or more even being those
classes with no members or empty. It is good to know.

Regards,
lieve


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Öö Tiib  
View profile  
 More options Sep 23 2012, 5:25 pm
Newsgroups: comp.lang.c++
From: Öö Tiib <oot...@hot.ee>
Date: Sun, 23 Sep 2012 14:25:44 -0700 (PDT)
Local: Sun, Sep 23 2012 5:25 pm
Subject: Re: Multiple Inheritance vs. Interface

On Sunday, 23 September 2012 23:40:42 UTC+3, lieve again  wrote:
> Ok, so the main reason for not implementing multiple inheritance
> (without workarounds) is the complexity added to the programmers
> (learning curve) and to the compiler developers (diamond
> problem, ...).

Yes, that is the reason. Also the solution, virtual inheritance is
not too efficient nor simple.

> I thought maybe making the interfaces pure virtual,
> there was a way to avoid the extra vpointers and I wanted to know how.
> Then if I start adding pure virtual classes to impose the derived
> classes with some kind of features like:
> class Derived : implements Readable, Writeable, Comparable,
> Convertible ...
> regardless of the programming language, we are ending with instances
> of the derived classes having 20 bytes or more even being those
> classes with no members or empty. It is good to know.

The bytes actually are cheap these days ... unless you write for some 8 bit
controller. On common platforms most of the memory goes into visuals
and sounds and helper texts and other massive data like that. Couple of bytes
for vtables of object that manages such data are usually not worth talking
about.

OTOH on the 8-bit controllers where you really count bytes you do not have
much need for such large class hierarchy anyway.


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
lieve again  
View profile  
 More options Sep 26 2012, 2:58 am
Newsgroups: comp.lang.c++
From: lieve again <hablando...@gmail.com>
Date: Tue, 25 Sep 2012 23:58:10 -0700 (PDT)
Local: Wed, Sep 26 2012 2:58 am
Subject: Re: Multiple Inheritance vs. Interface
On 23 Sep., 23:25, Öö Tiib <oot...@hot.ee> wrote:

Ok, so its a problem suffered from all the actual programming
languages, I think it could be a kind of limitation to obtain so big
objects, but its so.
Maybe the way to impose some kind of properties or functions to a
class without the vpointers replication penalty is the concepts
extension of C++11.

Regards,


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Stuart  
View profile  
 More options Sep 26 2012, 5:39 pm
Newsgroups: comp.lang.c++
From: Stuart <DerTop...@web.de>
Date: Wed, 26 Sep 2012 23:39:11 +0200
Local: Wed, Sep 26 2012 5:39 pm
Subject: Re: Multiple Inheritance vs. Interface
On 9/23/12 "lieve again" wrote:
[snip]

> I thought maybe making the interfaces pure virtual,
> there was a way to avoid the extra vpointers and I wanted to know how.
> Then if I start adding pure virtual classes to impose the derived
> classes with some kind of features like:
> class Derived : implements Readable, Writeable, Comparable,
> Convertible ...
> regardless of the programming language, we are ending with instances
> of the derived classes having 20 bytes or more even being those
> classes with no members or empty. It is good to know.

There is one way to avoid object bloat, but you are not going to like it :-)

The following code uses a hand-made vtable substitute. For that reason I
had to introduce a special pointer type which stores the class's ID
together with the pointer to the object. This class ID is assigned by
hand, so that this scheme will only work for class hierarchies that will
not get extended (if you want to use this for extendable class
hierarchies, you'd have to use growable look-up table instead of a fixed
array of method pointers, but then you had better use objective-C++).

Note that the base class Base in my example must not contain any virtual
methods, or else the class Derived will get bloated again. Of course,
this makes the code really awfull to look at. However, most of it may
get generated by a some clever pre-processor magic.

Since the full code may cause some shock, I'll give a short summary:

class Base {
public:
     void foo () {std::cout << "Base::foo\n";}
     BasePtr operator& ();

};

class Derived : public Base {
public:
     void foo () {std::cout << "Derived::foo\n";}
     DerivedPtr operator&();

}

Base1Ptr, Base2Ptr and DerivedPtr and those special pointer types that
allow us to do the following:

void invokeFooVirtually (const BasePtr& b) {
     b.foo();

}

int main () {
     Base b;
     Derived d;
     invokeFooVirtually(&b);
     invokeFooVirtually(&d);

}

will print:

Base1::foo
Derived::foo

There is a price you have to pay: The pointer types are now twice as
large (the xxxPtr contains not only the raw pointer but also the class's
ID). Furthermore the cast from DerivedPtr to BasePtr has to call the
cast operator of DerivedPtr.

A compilable example with two base classes and some members:

#include <iostream>

class Base1Ptr {
protected:
     static const int classID;
     class Base1* ptr;
     int objectID;
public:
     Base1Ptr (class Base1* ptr, int objectID = classID)
       : ptr(ptr), objectID(objectID) {}
     void foo () const;

};

const int Base1Ptr::classID = 0;

class Base1 {
protected:
     int base1Int;
public:
     Base1 (int i) : base1Int(i / 2) {}
     void foo () { std::cout << "Base1::foo with base1Int = "
                             << base1Int << "\n";}
     Base1Ptr operator& () {return Base1Ptr(this);}

};

class Base2Ptr {
protected:
     static const int classID;
     class Base2* ptr;
     int objectID;
public:
     Base2Ptr (class Base2* ptr, int objectID = classID)
       : ptr(ptr), objectID(objectID) {}
     void bar () const;
};

const int Base2Ptr::classID = 0;

class Base2 {
protected:
     int base2Int;
public:
     Base2(int i) : base2Int(i * 2){}
     void bar () { std::cout << "Base2::bar with base2Int = "
                             << base2Int << "\n";}
     Base2Ptr operator* ();

};

class DerivedPtr {
     class Derived* ptr;
     int objectID;
public:
     DerivedPtr (class Derived* ptr, int objectID)
       : ptr(ptr), objectID(objectID) {}
     operator Base1Ptr();
     operator Base2Ptr();

};

class Derived : public Base1, public Base2 {
     static const int classID;
public:
     Derived (int i) : Base1(i), Base2(i){}
     void foo () { std::cout << "Derived::foo with base1Int = "
                             << base1Int << "and base2Int = "
                             << base2Int << "\n";}
     void bar () { std::cout << "Derived::bar\n";}
     DerivedPtr operator&() {return DerivedPtr(this,classID);}

};

const int Derived::classID = 1; // std::max(Base1::classID,
                                 //          Base2::classID) + 1;
DerivedPtr::operator Base1Ptr() {return Base1Ptr(ptr, objectID);}
DerivedPtr::operator Base2Ptr() {return Base2Ptr(ptr, objectID);}

// Base1Ptr uses the following table to look up
// the correct member function.
typedef void (Base1::*FooPtr)(void);
FooPtr fooVTable[] = {&Base1::foo, (FooPtr)&Derived::foo};
void Base1Ptr::foo () const
{
     FooPtr targetFooFunction = fooVTable[objectID];
     (ptr->*targetFooFunction)();

}

// The same goes for Base2.
typedef void (Base2::*BarPtr)(void);
BarPtr barVTable[] = {&Base2::bar, (BarPtr)&Derived::bar};
void Base2Ptr::bar () const
{
     BarPtr targetFooFunction = barVTable[objectID];
     (ptr->*targetFooFunction)();

}

// Note that the cast of the addresses of members of Derived
// to addresses of members of Base results in UB.
// However, the results are pretty much as expected.

void invokeFoo (Base1* b) {
     b->foo();

}

void invokeFooVirtually (const Base1Ptr& b) {
     b.foo();

}

void invokeBarVirtually (const Base2Ptr& b) {
     b.bar();

}

int main () {
     Base1 b(3);
     Derived d(42);

     std::cout << "sizeof(Base1) == " << sizeof(Base1) << "\n";
     std::cout << "sizeof(Derived) == " << sizeof(Derived) << "\n";
     std::cout << "sizeof(Base1*) == " << sizeof(Base1*) << "\n";
     std::cout << "sizeof(Base1Ptr) == " << sizeof(Base1Ptr) << "\n";
     std::cout << "sizeof(Derived*) == " << sizeof(Derived*) << "\n";
     std::cout << "sizeof(DerivedPtr) == " << sizeof(DerivedPtr) << "\n";

     invokeFooVirtually(&b);
     invokeFooVirtually(&d);
     invokeBarVirtually(&d);

}

Regards,
Stuart

 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
lieve again  
View profile  
 More options Sep 30 2012, 7:27 am
Newsgroups: comp.lang.c++
From: lieve again <hablando...@gmail.com>
Date: Sun, 30 Sep 2012 04:27:41 -0700 (PDT)
Local: Sun, Sep 30 2012 7:27 am
Subject: Re: Multiple Inheritance vs. Interface
On 26 sep, 23:39, Stuart <DerTop...@web.de> wrote:

Interesting (and complex) example. I think, I have understood it, its
a way to avoid the extra vpointers in class by make the base pointers
"fatter". Probably some programming language have implemented multiple
inheritance that way.

Looking the code, another way to do it were:

// typedef void (Base1::*FooPtr)(void);
// FooPtr fooVTable[] = {&Base1::foo, (FooPtr)&Derived::foo};
void Base1Ptr::foo () const
{
     FooPtr targetFooFunction = fooVTable[objectID];
     (ptr->*targetFooFunction)();

     if(this->objectID != 0){
         Derived* derived = static_cast<Derived*>(ptr);
         derived->foo();
     }else // not derived, call it normally
         ptr->foo();

}

With the objectID we already know the true class of the base pointer,
so we could simply convert it safely to the derived class, in that way
we can avoid the pointer to member functions which they are a little
bit complicated. Instead we can made a table of int like an index to
know the true class or at least if we need to use virtual functions,
something like:
enum Index { BASE1 = 0, BASE2 = 1, BASE3 = 2, DERIVED = 3};

an depending of the table answer, convert it to the right class.

We could store the object id directly in the  Base1, and forget about
the BasePtr and the conversion operators, but this would made the base
classes bigger, so the Base1 approach its better: we make the pointer
fatter only if an conversion from the derived class to a base class
take place.

Thanks!!


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Cholo Lennon  
View profile  
 More options Sep 30 2012, 11:04 pm
Newsgroups: comp.lang.c++
From: Cholo Lennon <chololen...@hotmail.com>
Date: Mon, 01 Oct 2012 00:04:02 -0300
Local: Sun, Sep 30 2012 11:04 pm
Subject: Re: Multiple Inheritance vs. Interface
On 09/22/2012 03:10 PM, Richard Damon wrote:

That's why VC++ has an extension, __declspec(novtable) to mark abstract
classes intended to be used as interfaces.

> The main reason many languages don't implement multiple inheritance (but
> maybe Interfaces) is NOT object size, but language a program complexity.

--
Cholo Lennon
Bs.As.
ARG

 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Richard Damon  
View profile  
 More options Oct 1 2012, 8:45 am
Newsgroups: comp.lang.c++
From: Richard Damon <news.x.richardda...@xoxy.net>
Date: Mon, 01 Oct 2012 08:45:55 -0400
Local: Mon, Oct 1 2012 8:45 am
Subject: Re: Multiple Inheritance vs. Interface
On 9/30/12 11:04 PM, Cholo Lennon wrote:

> On 09/22/2012 03:10 PM, Richard Damon wrote:

>> When inheriting from Interfaces, the difference is that the Interface
>> never needs to exist as a discrete object, so there isn't a need to save
>> a vtable pointer for each Interface. The Interface routine likely need a
>> pointer into the base class vtable to the vtable for that interface, but
>> that should be computable from the object normal vtable pointer.

> That's why VC++ has an extension, __declspec(novtable) to mark abstract
> classes intended to be used as interfaces.

Unfortunately, __declspec(novtable) does NOT remove the vtable from the
object, but the removes the instruction to initialize the pointer from
the constructor, and thus likely removes the vtable itself (which occurs
only once per class, not once per object, so only a small savings). The
class object will still need to have a vtablepointer in it, so members
of that class can find their virtual functions.

The "interface" class members still need to be passed a "this" object of
the appropriate type, and that object needs to have a vtablepointer in
it to find the virtual functions. The problem here is that the "inteface
class" isn't something different than a regular class.

The typical difference with real interface classes in other languages is
that those classes don't take a pointer to the interface class object,
but to the full object, they "know" that they are going to be used as a
"mixin" class. They will normally take as their calling sequence API a
pointer to the object (not their "sub object", as those language tend
not to define a thing called a "sub object", and a pointer to the
section of the vtable which acts as the vtable for interface. Since
interfaces normally can not have member variables, only functions, there
is no need to have a pointer to that.


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pavel  
View profile  
 More options Oct 7 2012, 4:57 pm
Newsgroups: comp.lang.c++
From: Pavel <pauldontspamt...@removeyourself.dontspam.yahoo>
Date: Sun, 07 Oct 2012 16:57:46 -0400
Local: Sun, Oct 7 2012 4:57 pm
Subject: Re: Multiple Inheritance vs. Interface

I think the above is not accurate. C++ code does suffer performance penalties
from using multiple inheritance. Moreover, and what's especially frustrating,
even the code that does not use multiple inheritance (in fact, any code using
virtual functions) suffers from at least one performance penalty imposed by the
way C++ supports multiple inheritance: the necessity to read the offset of the
call target within the object of the most-derived class overriding the virtual
method and subtracting this offset from the passed pointer to let the virtual
function implementation access to the object it expects.

Languages with single inheritance can assign a single offset from the start of
the virtual table of the most-derived class of an object to the start of the
slice of that class' virtual table correspondent to the virtual table of any of
its bases. This effectively means that any class in such a language can have a
single virtual table and the objects of the most derived class and the
correspondent objects of all its base classes can have a single address.

Complicating C++ specification by introducing special kind of classes that would
be forbidden from being used as base classes in multiple inheritance (or, even
more complex but more rewarding as well -- the classes that cannot be used as
other-than-first base classes in multiple inheritance) could eliminate this
penalty for the language users who do not use multiple inheritance.

C++ does not have a chance of assigning any virtual function once defined in a
class a single offset in a virtual table; therefore, it has to have multiple
virtual tables. As it is, that is without the complication mentioned above, C++
can not let its compiler know at a virtual call site that the call is on the
object that is the first base of the most-derived class; hence the necessity to
always read and apply the offset at run-time.

Languages with single inheritance and interfaces still have same performance
advantages as languages without interfaces for the classes that do not implement
interfaces (that is, they live to the promise of not imposing cost of a feature
on the code that does not use it better than C++ does). For a class that does
implement interfaces, the implementations have a choice of either avoiding space
cost but making calls by interface significantly more expensive or adding
pointers to individual "interface virtual tables" to the layout of an object of
such a class and having calls by an interface only one indirection more
expensive than regular virtual calls. I believe Java 1.0 took the first path and
Java 1.1 and all its further versions took the second.

-Pavel


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Richard Damon  
View profile  
 More options Oct 7 2012, 11:19 pm
Newsgroups: comp.lang.c++
From: Richard Damon <news.x.richardda...@xoxy.net>
Date: Sun, 07 Oct 2012 23:19:11 -0400
Local: Sun, Oct 7 2012 11:19 pm
Subject: Re: Multiple Inheritance vs. Interface
On 10/7/12 4:57 PM, Pavel wrote:

> I think the above is not accurate. C++ code does suffer performance
> penalties from using multiple inheritance. Moreover, and what's
> especially frustrating, even the code that does not use multiple
> inheritance (in fact, any code using virtual functions) suffers from at
> least one performance penalty imposed by the way C++ supports multiple
> inheritance: the necessity to read the offset of the call target within
> the object of the most-derived class overriding the virtual method and
> subtracting this offset from the passed pointer to let the virtual
> function implementation access to the object it expects.

This is incorrect. It is possible to setup the virtual table so that
virtual functions based on the 1st base class make direct calls to the
destination functions (since no pointer adjustment is needed) but if a
class is the later base, there are actually 2 tables of pointers to the
functions, one pointer to by the base sub object, and a second one, part
of the table pointed to by the 1st base object. These two different
tables point to different points, one being to a "thunk" that adjusts
the this pointer, and the other which doesn't.

Base class functions, which see the this pointer as the later base
object, only have the base pointer in that base object, this version
does not adjust the pointer if the function was last defined under that
base object, but if the function has been overridden since the multiple
inheritance, it is a thunk which adjust the this pointer and then goes
to the override.

Functions after the multiple inheritance use the pointer in the first
base class, which does the reverse, having a thunk if the function was
last overridden before the multiple inheritance, and a direct connection
if after.

Zero cost unless there is multiple inheritance, and then only for calls
to functions that do require adjusting of the this pointer.


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Stuart  
View profile  
 More options Oct 8 2012, 3:35 am
Newsgroups: comp.lang.c++
From: Stuart <DerTop...@web.de>
Date: Mon, 08 Oct 2012 09:35:31 +0200
Local: Mon, Oct 8 2012 3:35 am
Subject: Re: Multiple Inheritance vs. Interface

[the OP, "lieve again", observed the problem that multiple inheritance
of either interface or non-interface classes leads to object bloat due
to the necessity to stuff objects with multiple vtables]

lieve again wrote:
>> Ok, so its a problem suffered from all the actual programming
>> languages, I think it could be a kind of limitation to obtain so big
>> objects, but its so.
>> Maybe the way to impose some kind of properties or functions to a
>> class without the vpointers replication penalty is the concepts
>> extension of C++11.
On 10/7/12 Pavel wrote:
> I think the above is not accurate. C++ code does suffer performance
> penalties from using multiple inheritance. Moreover, and what's
> especially frustrating, even the code that does not use multiple
> inheritance (in fact, any code using virtual functions) suffers from at
> least one performance penalty imposed by the way C++ supports multiple
> inheritance: the necessity to read the offset of the call target

What's the call target? Never heard this term.

> within
> the object of the most-derived class overriding the virtual method and
> subtracting this offset from the passed pointer to let the virtual
> function implementation access to the object it expects.

I don't get what you mean. Can you give an example?

> Languages with single inheritance can assign a single offset from the
> start of the virtual table of the most-derived class of an object to the
> start of the slice of that class' virtual table correspondent to the
> virtual table of any of its bases.

Yeah, for C++ this offset will always be zero.

> This effectively means that any class
> in such a language can have a single virtual table and the objects of
> the most derived class and the correspondent objects of all its base
> classes can have a single address.

Right. So casting a Derived* pointer to a Base* pointer for
single-inheritance chains will always be a noop under C++. I don't see
any kind of performance penalty.

[snip]

> C++ does not have a chance of assigning any virtual function once
> defined in a class a single offset in a virtual table; therefore, it has
> to have multiple virtual tables. As it is, that is without the
> complication mentioned above, C++ can not let its compiler know at a
> virtual call site that the call is on the object that is the first base
> of the most-derived class; hence the necessity to always read and apply
> the offset at run-time.

Which offset are you talking about? Can you give an example (preferably
for the Intel architecture)?

Regards,
Stuart


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pavel  
View profile  
 More options Oct 10 2012, 12:29 am
Newsgroups: comp.lang.c++
From: Pavel <pauldontspamt...@removeyourself.dontspam.yahoo>
Date: Wed, 10 Oct 2012 00:28:52 -0400
Local: Wed, Oct 10 2012 12:28 am
Subject: Re: Multiple Inheritance vs. Interface

The pointer to the base class on which a virtual function is called.

>> within
>> the object of the most-derived class overriding the virtual method and
>> subtracting this offset from the passed pointer to let the virtual
>> function implementation access to the object it expects.

> I don't get what you mean. Can you give an example?

Because the compiler does not know (generally, Richard gave a good algo that can
solve the issue -- but for cost) whether the base is the first base in the
particular most-derived class, it has to read the offset of the sub-object in
the object in which the virtual table is defined and subtract it from given
pointer to the base class, at run-time.

>> Languages with single inheritance can assign a single offset from the
>> start of the virtual table of the most-derived class of an object to the
>> start of the slice of that class' virtual table correspondent to the
>> virtual table of any of its bases.

> Yeah, for C++ this offset will always be zero.

For calling a virtual functions defined in a class with multiple bases by base
pointer, it will be zero only if the base is the first base (assuming without
loss of generality that the compiler allocates first base at the lowest
address); otherwise it will be something else. But, even if it is zero, it is
not known in advance to the compiler so the code will still have to read that
zero from memory and subtract it. That extra memory read can be relatively
expensive (subtraction is usually not).

>> This effectively means that any class
>> in such a language can have a single virtual table and the objects of
>> the most derived class and the correspondent objects of all its base
>> classes can have a single address.

> Right. So casting a Derived* pointer to a Base* pointer for single-inheritance
> chains will always be a noop under C++.

Right.
I don't see any kind of performance
> penalty.'

The performance penalty will is incurred to cat Base* to Derived* which is what
happens when you call Derived's overridden virtual function by pointer to Base.

> [snip]

>> C++ does not have a chance of assigning any virtual function once
>> defined in a class a single offset in a virtual table; therefore, it has
>> to have multiple virtual tables. As it is, that is without the
>> complication mentioned above, C++ can not let its compiler know at a
>> virtual call site that the call is on the object that is the first base
>> of the most-derived class; hence the necessity to always read and apply
>> the offset at run-time.

> Which offset are you talking about? Can you give an example (preferably for the
> Intel architecture)?

It's more compiler-specific than hardware-platform-specific. Imagine, compiler
lays out objects with virtual functions by putting virtual table pointer before
an object; and, for multiple inheritance, it places base sub-objects at the
beginning of derived objects, in the order of its base specifier list. Then for
these classes:

// file b.h
struct B { int b; virtual int getBOffset() const
        { return 0; }

};

// file b2.h
struct B2 { int b2; };
// file d.h
#include "b.h"
#include "b2.h"
struct D: public B, public B2 { int d; virtual int getBOffset() const; };
// file d.cpp
#include "d.h"
int D::getBOffset() const {
  return (const char*)this - (const char*)(const B*)this;
// not zero, most likely sizeof(int)
}

// file d2.h
#include "b.h"
#include "b2.h"
struct D2: public B2, public B { int d2; virtual int getBOffset() const; };
// file d2.cpp
#include "d2.h"
int D2::getBOffset() const {
  return (const char*)this - (const char*)(const B*)this;
// most likely, zero
}

// file x.cpp
#include "d.h"
#include "d2.h"
B *createB1() { return new D1(); }
B *createB2() { return new D2(); }
// file client.cpp
#include "b.h"
#include "x.h"
B *createB1();
B *createB2();
B *bPtr = createB1();
int o1 = bPtr->getBOffset();
B *bPtr2 = createB2();
int o2 = bPtr2->getBOffset();

Above, we know that to call bPtr2->getBOffset() (which is actually
D2::getBOffset()) compiler does not have to subtract anything from *bPtr2, but
compiler does not (D and D2 are not visible in client.cpp and neither are the
definitions of createB1 or createB2. Therefore, compiler has to generate th code
that reaches for the virtual table of bPtr2 and retrieve that zero (sometimes
stored at some negative address in the virtual table and sometimes in other
ways). As for the clever trick with double-virtual table and thunks described by
Richard Damon to avoid that, it will work but it comes at some cost for calling
virtual functions on non-second base and thus is not always employed (I will
stop at it later in the answer to his post).

> Regards,
> Stuart

HTH
-Pavel

 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pavel  
View profile  
 More options Oct 10 2012, 1:36 am
Newsgroups: comp.lang.c++
From: Pavel <pauldontspamt...@removeyourself.dontspam.yahoo>
Date: Wed, 10 Oct 2012 01:36:09 -0400
Local: Wed, Oct 10 2012 1:36 am
Subject: Re: Multiple Inheritance vs. Interface

True but thunk approach comes at higher cost for calling at non-first base of
extra jump as compared with 'classic' approach. Extra jump in chunk is
equivalent to at least one extra memory read (only to instruction instead of
data cache) and some instruction decoding. The approach also somewhat increases
memory usage with the second virtual table and thunks. But you are right about
zero-cost if calls by non-first base are not used -- I completely forgot about
thunk approach.

-Pavel


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Stuart  
View profile  
 More options Oct 10 2012, 9:02 am
Newsgroups: comp.lang.c++
From: Stuart <DerTop...@web.de>
Date: Wed, 10 Oct 2012 15:02:46 +0200
Local: Wed, Oct 10 2012 9:02 am
Subject: Re: Multiple Inheritance vs. Interface

>> On 10/7/12 Pavel wrote:
>>> C++ code does suffer performance
>>> penalties from using multiple inheritance. Moreover, and what's
>>> especially frustrating, even the code that does not use multiple
>>> inheritance (in fact, any code using virtual functions) suffers from at
>>> least one performance penalty imposed by the way C++ supports multiple
>>> inheritance: the necessity to read the offset of the call target  within
>>> the object of the most-derived class overriding the virtual method and
>>> subtracting this offset from the passed pointer to let the virtual
>>> function implementation access to the object it expects.

I still don't agree. If you use single-inheritance under C++, there is
no performance penalty compared to any vtable-based programming language
which would result from the fact that C++ provides multiple-inheritance.
Since the vtables and the data members of classes can be laid out by the
compiler in such a fashion that any "this" pointer never has to be
adjusted in a single-inheritance hierarchy, C++ is as effecient as it
ever gets.

[snip]

> Because the compiler does not know whether the base is the first
> base in the particular most-derived class, ... [snip]

If the compiler wants to do anything with a class, it needs to know the
complete definition of the class and all its base classes. So it knows
whether a certain base class is the first base class of another class or
not. What you probably tried to say is that the compiler, receiving a
pointer to Base* cannot know whether the real type of the object is Base
or Derived, so a "this"-pointer adjustment may have to be performed when
any of Derived's methods should be invoked.

Note that this adjustment is most likely done inside a "thunk"-method
which simply adjusts the "this" pointer and invokes Derived's
implementation method that does not need to adjust the "this" pointer.

Like so:

class Base1 {
   int b1;
   virtual void print1 ();

};

class Base2 {
   int b2;
   virtual void print2 ();
};

class Derived : Base1, Base2 {
   int derived;
   virtual void print1 ();
   virtual void print2 ();

};

     ,____________________  ___________  ,________________________
+0  |        |vtable    |->. print1  |  |void Derived::print2 { |
     |Base1   |----------|  | print2  |->|  std::cout << derived;|
+4  |        |int b1;   |  |_________|  |//&derived == this+16; |
     |________|__________|  ___________  |}                      |
+8  |        |vtable    |->| print2  |  |_______________________|
     |Base2   |----------|  |_________|  ,________________________
+12 |        |int b2;   |         |     |__asm {                |
     |________|__________|         |---->|  this -= 8;           |
+16 |int derived;       |               |  call Derived::print2 |
     |___________________|               |}                      |
                                         |_______________________|

  And even that is an implementation detail: A compiler writer may
choose to generate Derived::print multiple times, each version using its
own set of offsets. This would eliminate the need for thunking code
completely at the cost of larger executables:
     ,____________________  ___________  ,________________________
+0  |        |vtable    |->. print1  |  |void Derived::print2 { |
     |Base1   |----------|  | print2  |->|  std::cout << derived;|
+4  |        |int b1;   |  |_________|  |//&derived == this+16; |
     |________|__________|  ___________  |}                      |
+8  |        |vtable    |->| print2  |  |_______________________|
     |Base2   |----------|  |_________|
+12 |        |int b2;   |         |     ,________________________
     |________|__________|         |---->|void Derived::print2 { |
+16 |int derived;       |               |  std::cout << derived;|
     |___________________|               |//&derived == this+8;  |
                                         |}                      |
                                         |_______________________|

Regards,
Stuart


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pavel  
View profile  
 More options Oct 10 2012, 11:43 pm
Newsgroups: comp.lang.c++
From: Pavel <pauldontspamt...@removeyourself.dontspam.yahoo>
Date: Wed, 10 Oct 2012 23:43:22 -0400
Local: Wed, Oct 10 2012 11:43 pm
Subject: Re: Multiple Inheritance vs. Interface

No, it does not need to know "the complete definition". I am not sure about
"anything", but specifically for generating code that calls a virtual function
all the compiler needs to know is how to calculate the address of a particular
subobject of the object of the most-derived class pointed by the pointer given
to compiler. That particular subobject is uniquely defined by two requirements:
a) it contains the sub-subobject pointed to the given pointer as one of its bases
b) of all such sub-objects it is of the most derived class that overrode the
virtual function being called

such subobject can be calculated by
- subtracting an offset stored in the virtual table pointed by given pointer,
from that given pointer (that offset can be stored in the table in more than one
way)
- or by creating multiple virtual tables where some entries (those allocated by
the classes where the pointer's type is their first base) point to the actual
functions and the others point to pointer-adjusting thunks that forward control
to that original function after the adjustment.

None of the two ways is in general faster than the other (although I admit the
"thunk" way avoids direct penalties for calling virtual by pointers on first base).

So it knows whether a certain

> base class is the first base class of another class or not.

No, in general it does not know it at the call site. In the example you snipped
form my previous post for no reason this fact was clearly demonstrated:

When compiler is to generate the code for the virtual call

bPtr->getBOffset() or bPtr2->getBOffset()

it does not have access to the definition of the most-derived class of the
object pointed to by bPtr.

The call site is in client.cpp. bPtr and bPtr2 point to an object of either
class D (that has B as the first base) or D2 (that has it as the second base)
but the compiler does not have definition of either of them in view because
these definitions are in d.h and d2.h, respectively, and client.cpp does not
include either of these two headers, directly or indirectly. Thus, compiler has
no way of knowing whether bPtr and bPtr2 point to the first base of their most
derived classes or not.

What you probably

> tried to say is that the compiler, receiving a pointer to Base* cannot know
> whether the real type of the object is Base or Derived,

It is a true statement by itself, but it is not what I am saying.

so a "this"-pointer

> adjustment may have to be performed when any of Derived's methods should be
> invoked.

> Note that this adjustment is most likely done inside a "thunk"-method which
> simply adjusts the "this" pointer and invokes Derived's implementation method
> that does not need to adjust the "this" pointer.

Correct. Extra Invocation is not free though (even though it is not a full
invocation but just a jump on most architectures; but jump is not free either).
Simple adjustment will work better for non-first bases; thus some
implementations still use it; and some classes will work faster with it.

They can by that creating the code bloating issue (for a change, unrelated to
templates) with all its accompanying instructions-cache-use-efficiency issues.

Also, think of all the the nice linkage issues: what will be external names (or,
for that matter, addresses) for versions one and two of the Derived::print2? The
definitive address matters because in C++ you need to provide the capability of
calling-by-pointer-to-member, for the 1st and non-1st bases.

BTW such calls via pointer-to-members, even via the first base-pointer cannot be
made as efficient as they could be in the languages with the single inheritance
(such as C++ version ARM1 :-) ). It's all IMHO, of course, I have to be careful
now. This is because at the time pointer-to-member is initialized it is not yet
known whether it will be used with the pointer-to-the-first base or
pointer-to-the-higher-than-first base.

Long story short -- you cannot receive something (in our case classes that can
be interchangeably used as bases for both single and multiple inheritance) for
nothing (in our case no performance cost). (although I would agree it's worth to
die trying :-). )

each version using its own set of

-Pavel

 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Stuart  
View profile  
 More options Oct 11 2012, 4:01 am
Newsgroups: comp.lang.c++
From: Stuart <DerTop...@web.de>
Date: Thu, 11 Oct 2012 10:01:35 +0200
Local: Thurs, Oct 11 2012 4:01 am
Subject: Re: Multiple Inheritance vs. Interface

Stuart wrote:
>> If the compiler wants to do anything with a class, it needs to know
>> the complete
>> definition of the class and all its base classes.
On 10/11/12 Pavel wrote:
> No, it does not need to know "the complete definition".

You got me there. I meant to write complete declaration. Sorry.

> I am not sure
> about "anything", but specifically for generating code that calls a
> virtual function all the compiler needs to know is how to calculate the
> address of a particular subobject of the object of the most-derived
> class pointed by the pointer given to compiler.

This is an implementation detail of the compiler. "Thunking" compilers
do not have to do any additional work when they invoke a virtual function:
   Base* ptr = ...;
   ptr->foo ();

will result in the following (pseudo-) ASM:
   push ptr;
   call *[ptr + &foo]

where &foo gives the slot of the vtable where foo's address is stored.
In order to be able to generate such code, the compiler only has to know
the complete declaration of Base and Base's base classes (or else it
could not figure out the slot number of foo), even if ptr is initialized
with an instance of the hitherto unknown class Derived.

[snip]

> [...] I
> admit the "thunk" way avoids direct penalties for calling virtual by
> pointers on first base).

Here we go. If I may cite you from up-thread:

On 10/7/12 Pavel wrote:

 > I think the above is not accurate. C++ code does suffer performance
 > penalties from using multiple inheritance. Moreover, and what's
 > especially frustrating, even the code that does not use multiple
 > inheritance (in fact, any code using virtual functions) suffers from
 > at least one performance penalty imposed by the way C++ supports
 > multiple inheritance: the necessity to read the offset of the call
 > target [...]

Apparently you proved yourself wrong: It is not an inherent limitation
of the C++ language that it _must_ be slower for single-inheritance
hierarchies because it also supports multiple-inheritance. I think it
would be more correct to say that C++ can be as efficient as any
single-inheritance vtable-based language, provided the implementation of
the C++ compiler is good (IOW, uses thunks).

Regards,
Stuart


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
88888 Dihedral  
View profile  
 More options Oct 11 2012, 2:30 pm
Newsgroups: comp.lang.c++
From: 88888 Dihedral <dihedral88...@googlemail.com>
Date: Thu, 11 Oct 2012 11:30:00 -0700 (PDT)
Subject: Re: Multiple Inheritance vs. Interface
Pavel於 2012年10月8日星期一UTC+8上午4時57分53秒寫道:

I think testing  equivalent source programs in objetive c and c++
 under the same os can tell the differences.

linux or bsd can be


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Richard Damon  
View profile  
 More options Oct 11 2012, 11:49 pm
Newsgroups: comp.lang.c++
From: Richard Damon <news.x.richardda...@xoxy.net>
Date: Thu, 11 Oct 2012 23:49:08 -0400
Local: Thurs, Oct 11 2012 11:49 pm
Subject: Re: Multiple Inheritance vs. Interface
On 10/10/12 1:36 AM, Pavel wrote:

> True but thunk approach comes at higher cost for calling at non-first
> base of extra jump as compared with 'classic' approach. Extra jump in
> chunk is equivalent to at least one extra memory read (only to
> instruction instead of data cache) and some instruction decoding. The
> approach also somewhat increases memory usage with the second virtual
> table and thunks. But you are right about zero-cost if calls by
> non-first base are not used -- I completely forgot about thunk approach.

> -Pavel

Actually, most of the jumps can be removed, by placing the thunk
directly in front of the code for the subroutine.

In fact, if we define that when calling a virtual function, the this
pointer is always set to point to the sub object of the type where the
function was first declared as virtual, then the function receiving the
call knows exactly how to adjust the this pointer to point to the now
full object (of the type the function belongs to). and can place that
adjustment as a thunk just before the normal code for the function if we
want to allow it to be called "normally" in a non-virtual manner without
needed to pre-adjust the this pointer for those calls.

The only case where these adjustments become non-trivial would be in
cases of virtual inheritance where the pointer adjustment will need to
do a look up rather than a simple offset.

The simple procedure here is:

When calling a virtual function that was first defined in a non-first
base class (or a non-virtual function that is last defined in a
non-first base class), before making the call (this will be a simple
addition to the base pointer if we have non-virtual inheritance).

At the function side, if the function is a virtual function of a type
that has the initial definition in a non-first base class, include code
to adjust the this pointer from that base class to the actual class just
before the normal entry point, and have the vtable point there. The
regular entry point can be used for non-virtual calls, with the caller
passing the "normal" this pointer.

This is probably minimal overhead (works best if the this pointer is
passed in a register so the address offset is quick and easy). The only
case where you get "extra" adjustments is if you make the call through a
pointer to a class derived from one is non-first base derived from the
class that first defined the virtual function to a function defined in
class similarly after the multiple derivation, as you will adjust to the
base and then back.


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Pavel  
View profile  
 More options Oct 12 2012, 12:56 am
Newsgroups: comp.lang.c++
From: Pavel <pauldontspamt...@removeyourself.dontspam.yahoo>
Date: Fri, 12 Oct 2012 00:56:16 -0400
Local: Fri, Oct 12 2012 12:56 am
Subject: Re: Multiple Inheritance vs. Interface

I doubt it. I think the necessity to know the address of a particular subobject
follows from the necessity for the virtual functions to be able to access the
members of that particular sub-object (e.g. its data members); therefor this
necessity is hardly an implementation detail but a generic corollary from the
language definition.

  "Thunking" compilers do not

> have to do any additional work when they invoke a virtual function:
>    Base* ptr = ...;
>    ptr->foo ();

> will result in the following (pseudo-) ASM:
>    push ptr;
>    call *[ptr + &foo]

this works as long as *[ptr + &foo] evaluates the different address for every
different ptr at which foo() can be called for the object of the class (and
there can be plenty of these for a single definition of foo()). Which leaves the
implementation with choice to either use thunks with jumps (here is your extra
work) for all but one such different addresses or have multiple foo()s with the
ptr adjustment code inlined in each but one of them (there is no extra jump but
the duplication of the whole code of foo, again for every different ptr at which
the foo() can be called for the object of the class.

> where &foo gives the slot of the vtable where foo's address is stored. In order
> to be able to generate such code, the compiler only has to know the complete
> declaration of Base and Base's base classes (or else it could not figure out the
> slot number of foo), even if ptr is initialized with an instance of the hitherto
> unknown class Derived.

agree.

I have already admitted that the above statement was not accurate; I forgot
about the thunk approach; but not every implementation chooses it, and for
reason: as we saw above, thunks penalize calls by non-first bases by an extra
jump. As for the alternative of copying the entire function, it would penalize
the program by unconditionally bloating the code size.

It is not an inherent limitation of the

> C++ language that it _must_ be slower for single-inheritance hierarchies because
> it also supports multiple-inheritance. I think it would be more correct to say
> that C++ can be as efficient as any single-inheritance vtable-based language,
> provided the implementation of the C++ compiler is good (IOW, uses thunks).

As I hope I have shown above, the thunk approach has its own penalties as
compared to the direct pointer adjustment at the call site; thus I would not
unconditionally qualify it as "good". Different programs may perform better or
worse with this approach than with the direct adjustment.

> Regards,
> Stuart

-Pavel

 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Messages 1 - 25 of 30   Newer >
« Back to Discussions « Newer topic     Older topic »