Alfred Krohmer
Alfred Krohmer

Reputation: 153

Calling a C++ member function pointer: this-pointer gets corrupted

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.

Upvotes: 6

Views: 2999

Answers (3)

Steve Jessop
Steve Jessop

Reputation: 279265

It looks to me as though on your implementation, the first size(void*) bytes of an instance of the pointer-to-member-function type void (test::*) () just so happen, in this case, to be the address of a function in memory. As an implementation detail, that function is callable as if it were a free function with this as the first parameter. That's why conv3 appears to work.

However, your luck has run out when trying to copy those first sizeof(void*) bytes out into a different instance of the pointer-to-member-function type. The uninitialized garbage in the rest of conv2, once interpreted as the rest of a pointer-to-member-function after the initial code address, has made something go wrong. I suspect there are some flags and offsets in there, to record information about virtual functions and multiple- and virtual- inheritance. When that information is incorrect, stuff goes wrong.

Upvotes: 1

David Schwartz
David Schwartz

Reputation: 182769

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.

Upvotes: 6

Cl&#233;ment
Cl&#233;ment

Reputation: 324

Weirdly enough, here (under VS2005), the 1st and 3rd calls works fine but the 2nd (with conv3) fails with the this being corrupted.

Upvotes: 1

Related Questions