For an exercise I need to call a virtual method by way of the virtual method table (vtable). I'm experiencing some behavior I can't understand--the correct method is called, but with the wrong value for this
. Here's some code that first demonstrates the correct behavior by fetching the method pointer directly, then demonstrates the wonky behavior by using the vtable:
#include <cstdio>
class Thing
{
public:
virtual void foo()
{
std::printf("this:\t\t%p (in foo())\n", this);
}
};
typedef void (Thing::*METHOD_PTR)();
void print_method_ptr(METHOD_PTR ptr) {
// Helper function to display method pointers.
for (size_t i = 0; i < sizeof ptr; ++i)
printf("%d ", reinterpret_cast<char *>(&ptr)[i]);
}
void direct(Thing* thing_ptr) {
std::printf("In direct().\n");
// Get the pointer directly.
METHOD_PTR foo_ptr = &Thing::foo;
std::printf("&Thing::foo: ");
print_method_ptr(&Thing::foo);
std::printf("\nfoo_ptr: ");
print_method_ptr(foo_ptr);
if (foo_ptr == &Thing::foo) {
std::printf("\nfoo_ptr == &Thing::foo\n");
} else {
std::printf("\nfoo_ptr != &Thing::foo\n");
}
std::printf("thing_ptr:\t%p\n", thing_ptr);
(thing_ptr->*foo_ptr)();
}
void via_vtable(Thing* thing_ptr) {
std::printf("In via_vtable().\n");
// Retrieve foo_ptr from the vtable.
METHOD_PTR** thing_ptr_cast = reinterpret_cast <METHOD_PTR**> (thing_ptr);
METHOD_PTR* vtable_ptr = *thing_ptr_cast;
METHOD_PTR foo_ptr = vtable_ptr[0];
std::printf("&Thing::foo: ");
print_method_ptr(&Thing::foo);
std::printf("\nfoo_ptr: ");
print_method_ptr(foo_ptr);
if (foo_ptr == &Thing::foo) {
std::printf("\nfoo_ptr == &Thing::foo\n");
} else {
std::printf("\nfoo_ptr != &Thing::foo\n");
}
std::printf("thing_ptr:\t%p\n", thing_ptr);
(thing_ptr->*foo_ptr)();
}
int main()
{
Thing thing;
direct(&thing);
std::printf("\n");
via_vtable(&thing);
return 0;
}
The output:
In direct().
&Thing::foo: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
foo_ptr: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
foo_ptr == &Thing::foo
thing_ptr: 0x7fffc6054940
this: 0x7fffc6054940 (in foo())
In via_vtable().
&Thing::foo: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
foo_ptr: -42 9 64 0 0 0 0 0 53 84 104 105 110 103 0 0
foo_ptr != &Thing::foo
thing_ptr: 0x7fffc6054940
this: 0xe76e2f6d9d75 (in foo())
This is very confusing. My main questions:
- In
via_vtable()
, why doesfoo_ptr != &Thing::foo
? - Why does
foo()
still get called even though the method pointer is apparently incorrect? - When
foo()
is called via the vtable, why is the value forthis
incorrect? - Is there a better way to retrieve the method from the vtable?
For the exercise I've been directed to the Wikipedia page for Virtual method tables, which explains their memory layout for g++.
I'm using g++ 4.8.4 on 64-bit Linux.
EDIT: Using -fdump-class-hierarchy
with g++ suggests that the Wikipedia page is wrong about the vtable memory layout:
Vtable for Thing
Thing::_ZTV5Thing: 3u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI5Thing)
16 (int (*)(...))Thing::foo
However, that doesn't explain how foo()
was actually executed before (and it certainly doesn't explain the wrong this
in foo()
.) Also, if I change METHOD_PTR foo_ptr = vtable_ptr[0];
to METHOD_PTR foo_ptr = vtable_ptr[1];
(which should then be offset 16, since sizeof(METHOD_PTR)
is 16), I get a segmentation fault when I call the method.