简体   繁体   中英

how to use a method of a c++ class as a callback function

I am trying to use a class's method as a callback function in another class in Qt. This is what I want to do:

Class A: public QObject     
Q_OBJECT

public:
    virtual void callBackFunc() = 0;
}

Class B: public A {

public:
    B();
    ~B();

    void callBackFunc() {
        emit signal1();
    }
}

Class C : public QObject {
Q_OBJECT

private:
    A* m_b;

public:
    C() {
        m_b = new B();
    }
    ~C();
    void func() {
         otherFunc(1,2,m_b->callBackFunc); // An API provided by an external library: otherFunc(int,int,void (*function)(void));
    }
}

The above code doesn't compile. It throws the following error:

cannot convert 'A::callBackFunc' from type 'void (A::)()' to type 'void (*)()

How can I call callBackFunc in func() method of class C within otherFunc()?

If your API takes a void (*function)(void) , you cannot pass it a void (*A::function)(void) .

First one is a function , the second one is a class method (which needs an object to be used on). Only static class methods (which are like functions) could be used here.

Alternatively, you could use global static variables to identify the object on which callBackFunc has to be called. But be very carefull with that ( C::func must not be called recursively or from different threads...).

static B* objectToCallFuncOn = NULL;
void globalFunc()
{
    assert( objectToCallFuncOn );
    objectToCallFuncOn->callBackFunc();
}

void C::func() 
{
     objectToCallFuncOn = m_b;
     otherFunc(1,2,&globalFunc);
     objectToCallFuncOn = NULL;
}

As previous answer already said, a class method requires a this ptr. One way of doing it is with capturing 'this' with a C++11 lambda. Here is one example:

Callback with lambda capturing 'this' ptr

First of all, you can't pass a c++ style member function pointer to c style function pointer directly, but indirectly, there are two ways to achieve a goal.

  • static member method
    The c++ member function's address is based on the instance, it is

this + offset

so every member method must has this pointer, the compiler did this.

Class B: public A {
public:
    B();
    ~B();

    void callBackFunc() {
        emit signal1();
    }
}
B b;
b.callBackFunc();

it is amount to

callBackFunc(&b)

this is why your compiler occurs an error.
The static member method doesn't need "this" pointer, so you can pass it to c style function, just like @jpo38 said.

  • thunk

what is thunk? you can find it in the source of WTL. The conclusion is, you can use thunk pack "this" pointer. Here is an case (reference page)

//main.cpp
#include <iostream>
#include <Windows.h>
#include <process.h>
#include "Thunk.h"
#include "resource.h"
using namespace std;

/////////////////////////////////////////////////////////
//第一个:__cdecl 回调类型
/////////////////////////////////////////////////////////

typedef int (__cdecl* CB)(int n);

void output(CB cb)
{
    for(int i=0; i<3; i++){
        cb(i);
    }
}

class ACDCEL
{
public:
    ACDCEL()
    {
        void* pthunk = m_Thunk.Cdeclcall(this,&ACDCEL::callback);
        ::output(CB(pthunk));
    }

private:
    int __cdecl callback(int n)
    {
        cout<<"n:"<<n<<endl;
        return n;
    }

private:
    AThunk m_Thunk;
};

/////////////////////////////////////////////////////////
//第二个:__stdcall 回调类型:封装窗口类
/////////////////////////////////////////////////////////
class ASTDCALL
{
public:
    ASTDCALL()
    {
        void* pthunk = m_Thunk.Stdcall(this,&ASTDCALL::DialogProc);
        DialogBoxParam(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_DIALOG1),NULL,(DLGPROC)pthunk,0);
    }

private:
    INT_PTR CALLBACK DialogProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
    {
        switch(uMsg)
        {
        case WM_CLOSE:
            EndDialog(hWnd,0);
            return 0;
        }
        return 0;
    }
private:
    AThunk m_Thunk;
};

/////////////////////////////////////////////////////////
//第三个:__stdcall 回调类型:内部线程
/////////////////////////////////////////////////////////
class AThread
{
public:
    AThread()
    {
        void* pthunk = m_Thunk.Stdcall(this,&AThread::ThreadProc);
        HANDLE handle = (HANDLE)_beginthreadex(NULL,0,(unsigned int (__stdcall*)(void*))pthunk,(void*)5,0,NULL);
        WaitForSingleObject(handle,INFINITE);
        CloseHandle(handle);
    }

private:
    unsigned int __stdcall ThreadProc(void* pv)
    {
        int i = (int)pv;
        while(i--){
            cout<<"i="<<i<<endl;
        }
        return 0;
    }
private:
    AThunk m_Thunk;
};

int main(void)
{
    ASTDCALL as;
    ACDCEL ac;
    cout<<endl;
    AThread at;
    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM