David De Geyter
David De Geyter

Reputation: 23

How do I pass a member function to a function that expects a function pointer?

I am using a library that expects a function pointer for a callback function but I would like to pass a member function to it.

The way to do this seems to be by using std::bind and passing the target() as a parameter to the function expecting the function pointer, but I must be doing something wrong.

#include <iostream>
#include <functional>

using namespace std;

typedef void(__stdcall* ClassicFuncPtr)(int a, double b);

void CallMyFunc(ClassicFuncPtr f) {
    f(1, 2.0);
}

class MyClass {
public:
    void Test() {
        function<void(int, double)> f = bind(&MyClass::MemberFunc, this, placeholders::_1, placeholders::_2);
        CallMyFunc(*f.target<ClassicFuncPtr>()); // not working, i don't know why...
    }
private:
    void MemberFunc(int a, double b) {
        cout << "a=" << a << ", b=" << b << '\n';
    }
};

int main() {
    MyClass c;
    c.Test();
}

The target() returns a nullpointer.

Upvotes: 2

Views: 654

Answers (2)

Evg
Evg

Reputation: 26362

The problem here is that a member function expects a "hidden" first argument that is a this pointer. Loosely speaking, a member function

void Func(int a, double b);

is equivalent to a free function

void Func(MyClass* this, int a, double b);

There is no way to pass a this pointer via ClassicFuncPtr, and std::function tricks won't help you here. target() doesn't do any magic, it just returns a pointer to the stored function if types match, and in your code they don't, that's why you get a null pointer. std::bind returns a functional object (that stores this inside), but a functional object is quite distinct from a function pointer and can't be converted into one.

Given that you can't change the callback type, there is a pretty ugly and fragile work-around that uses a static variable to store the value of this pointer. It should give you the idea of how to make it work, at least in principle.

class MyClass {
public:
    void Test() {
        thisPtr = this;
        CallMyFunc(MemberFuncInvoker);
    }

private:
    inline static MyClass* thisPtr;

    static void MemberFuncInvoker(int a, double b) {
        thisPtr->MemberFunc(a, b);
    }

    void MemberFunc(int a, double b) {
        std::cout << "a = " << a << ", b = " << b << '\n';
    }    
};

Note that static member functions don't expect a hidden this argument and behave like free functions in this respect (due to the absence of this argument, you can't access non-static data members inside a static member function).

Demo


Typically, callbacks are accompanied with a void*-like parameter that can be used to pass a this pointer. For example, theEnumWindows function from WinAPI has the signature

BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam);

That lParam is passed to a callback

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam);

Then, inside a free (or static) callback function you could do:

reinterpret_cast<MyClass*>(lParam)->MyMemberFunction(hwnd);

Upvotes: 1

nhatnq
nhatnq

Reputation: 1193

It's not possible to do that.The type of pointer-to-member-function is different from pointer-to-function. For example:

  • It will be int (*)(int,double) if an ordinary function.
  • It will be int (MyClass ::*)(int,dobule) if a non-static member function of class MyClass

Note: In case that it’s a static member function, its type is the same as if it was an ordinary function int (*)(int,double).

In order to make it work somehow with the non-static member function. It also needs this pointer. Thus, you can rewrite the function as following

typedef void(MyClass::*ClassicFuncPtr)(int a, double b);

void CallMyFunc(MyClass* t, ClassicFuncPtr f) {
    (t->*f)(1, 2.0);
}

Upvotes: 1

Related Questions