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 forthisincorrect? - 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.