I need to convert some member function pointers to void*
pointers (because I need to push them to the Lua stack, but the problem is not Lua related).
I do this using a union
. But when I convert the member function pointers to a void*
and back again and then try to call the pointer with an instance of the class, the this
pointer gets corrupted. Strangly, this problem doesn't happen, if I convert the void*
pointer back into a C-style function pointer with a pointer to the class as it's first parameter.
Here is piece of code that demonstrates the problem:
#include <iostream>
using namespace std;
class test
{
int a;
public:
void tellSomething ()
{
cout << "this: " << this << endl;
cout << "referencing member variable..." << endl;
cout << a << endl;
}
};
int main ()
{
union
{
void *ptr;
void (test::*func) ();
} conv1, conv2;
union
{
void *ptr;
void (*func) (test*);
} conv3;
test &t = *new test ();
cout << "created instance: " << (void*) &t << endl;
// assign the member function pointer to the first union
conv1.func = &test::tellSomething;
// copy the void* pointers
conv2.ptr = conv3.ptr = conv1.ptr;
// call without conversion
void (test::*func1) () = conv1.func;
(t.*func1) (); // --> works
// call with C style function pointer invocation
void (*func3) (test*) = conv3.func;
(*func3) (&t); // --> works (although obviously the wrong type of pointer)
// call with C++ style member function pointer invocation
void (test::*func2) () = conv2.func;
(t.*func2) (); // `this' is the wrong pointer; program will crash in the member function
return 0;
}
That is the output:
created instance: 0x1ff6010
this: 0x1ff6010
referencing member variable...
0
this: 0x1ff6010
referencing member variable...
0
this: 0x10200600f
referencing member variable...
zsh: segmentation fault (core dumped) ./a.out
Is this a bug in the compiler (GCC)? I know that this conversion between void*
and (member) function pointers is not standard compliant, but the odd thing is, that it works, when converting the void*
to a C style function pointer.
Add these two lines to your code and the answer will be clear:
cout << "sizeof(void*)=" << sizeof(conv1.ptr) << endl;
cout << "sizeof(test::*)=" << sizeof(conv1.func) << endl;
The reason is simple. Consider:
class Base1
{
public:
int x;
void Foo();
Base1();
};
class Base2
{
public:
float j;
void Bar();
Base2();
};
class Derived : public Base1, public Base2
{
Derived();
};
When you call Foo
on a Derived
, the this
pointer must point to Base1::x
. But when you call Bar
on a Derived
, the this
pointer must point to Base2::j
! So a pointer to a member function must include both the address of the function and an "adjustor" to correct the this
pointer to point to an instance of the correct type of class that the function expects as the this
pointer.
You are losing the adjuster, causing the this
pointer to be adjusted randomly.