vtbl and vptr

T

Thomas Lenz

Hi group,

just curious: assuming i have a base class B with virtual functions and
other classes derived from B:

does this result in a vtbl / vptr's per class, or per object ?


thanks, Thomas
 
R

Ripunjay Tripathi

Hi group,

just curious: assuming i have a base class B with virtual functions and
other classes derived from B:

does this result in a vtbl / vptr's per class, or per object ?

thanks, Thomas


pointer to vtable is inserted by compiler per-object

Regards,
Ripunjay Tripathi
 
J

Juan Antonio Zaratiegui Vallecillo

Thomas Lenz escribió:
Hi group,

just curious: assuming i have a base class B with virtual functions and
other classes derived from B:

does this result in a vtbl / vptr's per class, or per object ?


thanks, Thomas

The information that belongs to the class will exits per class. *If*
this is implemented as a vtbl, there will be a vtbl per class.

Each object needs to know the class it belongs to, and thus it will
contain some way to do that. *If* this is implemented as a vptr, there
will be a vptr per object.

There may be other ways to solve virtuality problems, but always "...
give to Caesar what is Caesar's , and to God what is God's". That is,
information belonging to a class will exist in a per-class basis
(similar to an static data member), information belonging to an object
will exist within the object (similar to a non-static data member)

Best regards,

Zara
 
T

Thomas Lenz

am Dienstag 16 Dezember 2008 09:16 schrieb Juan Antonio Zaratiegui
Vallecillo:
Thomas Lenz escribió:

The information that belongs to the class will exits per class. *If*
this is implemented as a vtbl, there will be a vtbl per class.

Each object needs to know the class it belongs to, and thus it will
contain some way to do that. *If* this is implemented as a vptr, there
will be a vptr per object.

There may be other ways to solve virtuality problems, but always "...
give to Caesar what is Caesar's , and to God what is God's". That is,
information belonging to a class will exist in a per-class basis
(similar to an static data member), information belonging to an object
will exist within the object (similar to a non-static data member)

Best regards,

Zara

ok, so it's one vtbl per class.

Just to make sure i got the vptr issue right: my understanding so far is
that vptrs point to the right virtual function, based on the "real" object
type, not on the type of the pointer/reference that points/references to
the object. So is there one vptr per object PER (VIRTUAL) FUNCTION? or do
compilers (assuming they use the vptr approach) somehow manage to do this
with just one vptr per object (only)?

Thanks in advance,
Thomas
 
J

Juan Antonio Zaratiegui Vallecillo

Thomas Lenz escribió:
am Dienstag 16 Dezember 2008 09:16 schrieb Juan Antonio Zaratiegui
Vallecillo:
ok, so it's one vtbl per class.

Just to make sure i got the vptr issue right: my understanding so far is
that vptrs point to the right virtual function, based on the "real" object
type, not on the type of the pointer/reference that points/references to
the object. So is there one vptr per object PER (VIRTUAL) FUNCTION? or do
compilers (assuming they use the vptr approach) somehow manage to do this
with just one vptr per object (only)?

Usually, the vptr will be a pointer to the class vtbl. Such vtbl will
contain:

* typeid info.
* info to resolve dynamic_cast
* pointers to virtual functions, as overloaded for this particular
class. Only one pointer indirection should be necessary to apply the
function.

Remember this info is not the only information 'virtualised', you should
also take virtual base classes into account.

best regards,

Zara
 
R

Rolf Magnus

Thomas said:
am Dienstag 16 Dezember 2008 09:16 schrieb Juan Antonio Zaratiegui
Vallecillo:


ok, so it's one vtbl per class.

Just to make sure i got the vptr issue right: my understanding so far is
that vptrs point to the right virtual function,

No. They point to the vtable.
based on the "real" object type, not on the type of the pointer/reference
that points/references to the object. So is there one vptr per object PER
(VIRTUAL) FUNCTION? or do compilers (assuming they use the vptr approach)
somehow manage to do this with just one vptr per object (only)?

Yes, they do. That is actually what the vtable is for. It's (mostly) a table
of pointers to all the virtual functions in the class.
 
R

Rolf Magnus

Juan said:
Usually, the vptr will be a pointer to the class vtbl. Such vtbl will
contain:

* typeid info.
* info to resolve dynamic_cast
* pointers to virtual functions, as overloaded for this particular
class. Only one pointer indirection should be necessary to apply the
function.

Actually, it's at least 3 (might be more with multiple inheritance or
virtual inheritance). When doing a polymorphic call, you have a pointer to
the object to begin with. You need to dereference that to get to the vtable
pointer. Dereferencing that brings you to the pointer to the virtual
function to be called. And then this pointer needs to be dereferenced for
the actual function call.
 
T

Thomas Lenz

am Dienstag 16 Dezember 2008 13:37 schrieb Rolf Magnus:
Actually, it's at least 3 (might be more with multiple inheritance or
virtual inheritance). When doing a polymorphic call, you have a pointer to
the object to begin with. You need to dereference that to get to the
vtable pointer. Dereferencing that brings you to the pointer to the
virtual function to be called. And then this pointer needs to be
dereferenced for the actual function call.

Thanks Juan and Rolf for this detailed explanation! It helps me a lot to get
some deeper understanding of this virtual stuff...

Best regards,
Thomas
 
R

red floyd

Hi group,

just curious: assuming i have a base class B with virtual functions and
other classes derived from B:

does this result in a vtbl / vptr's per class, or per object ?

Technically, vtbl/vptr is an implementation detail. There is no
requirement
that a class even *have* a vtbl or vptr. I can imagine a naive (and
not particularly
efficient) implementation without one.
 
L

Laurent Deniau

Thomas Lenz escribió:> am Dienstag 16 Dezember 2008 09:16 schrieb Juan Antonio Zaratiegui




Usually, the vptr will be a pointer to the class vtbl. Such vtbl will
contain:

* typeid info.
* info to resolve dynamic_cast

and more like MI/VI offsets (thunks) and partial ctors/dtors for
classes inheriting from virtual base classes
* pointers to virtual functions, as overloaded

I would say "as overridden" (overloading is related to types).
for this particular
class. Only one pointer indirection should be necessary to apply the
function.

actually, 3 indirections and more with offsets adjustment for MI/VI.
Remember this info is not the only information 'virtualised', you should
also take virtual base classes into account.

which leads to more vtables.

For example assuming the two polymorphic classes B and D:

class B { ... };
class D : virtual B { ... };

D should have two vtables, two ctors for each "user" defined ctor and
instances should hold two vptrs.

regards,

ld.
 
L

Laurent Deniau

Technically, vtbl/vptr is an implementation detail.  There is no
requirement
that a class even *have* a vtbl or vptr.  I can imagine a naive (and
not particularly
efficient) implementation without one.

could you describe it shortly?

regards,

ld.
 
L

Laurent Deniau

Laurent Deniau <[email protected]> kirjutas:





When creating objects by 'new',

what do you mean by 'new', could you give details about the steps?
the pointer is registered in a global
lookup container corresponding to the actual class. All cases of virtual
dispatch will be implemented as ordinary functions according to the
pseudo-code:

int virtual_member_f1(void* this_ptr, int arg1) {
        if (global_register_for_triangle.contains(this_ptr)) {
            return triangle::f1(this_ptr, arg1);
        } else if (global_register_for_cube.contains(this_ptr)) {
                return cube::f1(this_ptr, arg1);
        } // etc...

This is not conformant. All virtual methods are "always" virtual in
your case. How do you manage virtual destructors and virtual member
functions called within constructors?
No vtables, no vptr-s. Adding new classes in dynamically loaded libraries
is left as an exercise to the reader.

I let as an exercise to the poster to understand why this approach
cannot work.

a+, ld.
 
E

Erik Wikström

When creating objects by 'new', the pointer is registered in a global
lookup container corresponding to the actual class.

Another, probably more efficient implementation would be to allocate all
classes of the same type from the same "region" of memory (think block
allocator) and when accessing a virtual function you mask out the least
significant bits of the object's address which gives the address to the
"region head" containing the vtable.
 
R

Rolf Magnus

Laurent said:
what do you mean by 'new', could you give details about the steps?

Uhm? Never heard of dynamic memory allocation in C++?
This is not conformant. All virtual methods are "always" virtual in
your case.

What is not conforming about that?
How do you manage virtual destructors and virtual member
functions called within constructors?

The same?
 
R

red floyd

could you describe it shortly?

Sure. Each virtual function is implemented as a function pointer
inside the class object. Note that they would not have to be
contiguous, hence, no "vtbl". Since there's no vtbl, no vptr.

e.g.:

struct S
{
int x;
virtual void f();
int y;
virtual void g();
};

would have the same layout as the C struct:


struct S
{
int x;
void (*f)();
int y;
void (*g)();
};

Again, no vtbl, because the pointers to virtual functions aren't
contiguous. No vtbl => no vptr.
 
J

James Kanze

When creating objects by 'new', the pointer is registered in a
global lookup container corresponding to the actual class.

'new' has nothing to do with it. The compiler generates extra
code in the constructor to insert the this pointer(s) into a map
(as keys).
All cases of virtual dispatch will be implemented as ordinary
functions according to the pseudo-code:
int virtual_member_f1(void* this_ptr, int arg1) {
if (global_register_for_triangle.contains(this_ptr)) {
return triangle::f1(this_ptr, arg1);
} else if (global_register_for_cube.contains(this_ptr)) {
return cube::f1(this_ptr, arg1);
} // etc...
No vtables, no vptr-s. Adding new classes in dynamically
loaded libraries is left as an exercise to the reader.

I doubt that that could be made to work. But a map from the
pair <function name, this> to the pair <this fixup, function
address> certainly could.
 
J

James Kanze

[...]
This is not conformant. All virtual methods are "always"
virtual in your case. How do you manage virtual destructors
and virtual member functions called within constructors?

The compiler generates code in the constructors and destructors
to deregister from one registry, and re-register in another.
Just as it generates code today to change the vptr.

The reason this can't be made to work, as is, is because it
implies that the compiler know all of the derived classes at one
point. (Theoretically, I guess, the linker could generate these
functions, but practically, I don't think that's reasonable.)
I let as an exercise to the poster to understand why this
approach cannot work.

If you drop separate compilation, it could.
 
J

James Kanze

Another, probably more efficient implementation would be to
allocate all classes of the same type from the same "region"
of memory (think block allocator) and when accessing a virtual
function you mask out the least significant bits of the
object's address which gives the address to the "region head"
containing the vtable.

Rather difficult for objects with auto or static lifetime, no?
Of course, you could probably recognize those from the address
too, and use Paavo's suggestion as a back-up method.
 
L

Laurent Deniau

    [...]
This is not conformant. All virtual methods are "always"
virtual in your case. How do you manage virtual destructors
and virtual member functions called within constructors?

The compiler generates code in the constructors and destructors
to deregister from one registry, and re-register in another.
Just as it generates code today to change the vptr.

James, you are underestimating the complexity of the virtual
inheritance ;-)

assuming the following polymorphic types (with virtual member
functions) all holding a long integer of the same name (e.g. A has an
'a', B has an 'b', etc):

struct A { ... };
struct B { ... };
struct AB : virtual A, virtual B { ... };
struct BA : virtual B, virtual A { ... };
struct ABBA : AB, BA { ... };

here is what the ABBA ctor and its partial ctor_ must do (the dtor and
dtor_ do nearly the same in the reverse order):

// C++
ABBA(long a, long b, long ab, long ba, long abba_) :
A(a), B(b), AB(a,b,ab), BA(a,b,ba), abba(abba_) {}

// C equivalent
void ABBA_ctor(register ABBA* const this,
long a, long b, long ab, long ba, long abba)
{
A_ctor(BASE(this,A), a); /* compiler + user */
B_ctor(BASE(this,B), b); /* compiler + user */
AB_ctor_((AB*)this, ABBA_vtt, a, b, ab); /* compiler + user */
BA_ctor_(BASE(this,BA), ABBA_vtt+3, a, b, ba); /* compiler + user */
this->vptr = ABBA_vtbl; /* compiler */
this->BA_vptr = ABBA_BA_vtbl; /* compiler */
this->A_vptr = ABBA_A_vtbl; /* compiler */
this->B_vptr = ABBA_B_vtbl; /* compiler */
this->abba = abba; /* user */
}

// C equivalent, partial ctor, vtt is a table of vtbl
void ABBA_ctor_(register ABBA* const this, const VTABLE* const vtt[],
long a, long b, long ab, long ba, long abba)
{
AB_ctor_((AB*)this, vtt+4, a, b, ab);
BA_ctor_(BASE(this,BA), vtt+7, a, b, ba);
this->vptr = vtt[0];
this->BA_vptr = vtt[1];
VBASE(this,ABBA_A)->vptr = vtt[2];
VBASE(this,ABBA_B)->vptr = vtt[3];
this->abba = abba;
}

This code is part of my (unfinish) paper on the C++ object model from
2004. The code is completed, compiles and works and demonstrate how
all the machinery works. It can be found there

http://cern.ch/laurent.deniau/oopc.html#CPPObjModel

it is based on the paper of Nathan Sidwell, ACCU 2003. The cited
"Inside the C++ Object Model"
from Stanley B. Lippman is obsolete (does not reflect models currently
used) but still very didactic.
The reason this can't be made to work, as is, is because it
implies that the compiler know all of the derived classes at one
point.  (Theoretically, I guess, the linker could generate these
functions, but practically, I don't think that's reasonable.)

Right. This is why I say that it cannot work.
If you drop separate compilation, it could.

Of course. All program analysis can do everything, but then you don't
need polymorphism at all ;-)

a+, ld.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top