如何使用c ++类的方法作为回调函数

时间:2015-11-18 16:00:47

标签: c++ qt callback

我正在尝试使用类的方法作为Qt中另一个类的回调函数。这就是我想要做的事情:

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));
    }
}

上面的代码没有编译。它会引发以下错误:

  

无法转换' A :: callBackFunc'来自类型' void(A ::)()'键入' void   (*)()

如何在otherFunc()中的C类func()方法中调用callBackFunc?

3 个答案:

答案 0 :(得分:3)

如果您的API使用void (*function)(void),则无法将其传递给void (*A::function)(void)

第一个是函数,第二个是类方法(需要使用一个对象)。这里只能使用静态类方法(类似函数)。

或者,您可以使用全局静态变量来标识必须调用callBackFunc的对象。 但要非常小心C::func不能递归调用或来自不同的线程......)。

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

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

答案 1 :(得分:0)

正如之前的回答所说,类方法需要这个ptr。一种方法是使用C ++ 11 lambda捕获'this'。这是一个例子:

Callback with lambda capturing 'this' ptr

答案 2 :(得分:0)

首先,你不能直接将 c ++样式成员函数指针传递给 c样式函数指针,但间接地,有两种方法可以实现一个目标。

  • 静态成员方法
    c ++成员函数的地址基于实例,它是
  

此+偏移

所以每个成员方法都必须有这个指针,编译器就这样做了。

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

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

它等于

callBackFunc(&b)

这就是您的编译器发生错误的原因 静态成员方法并不需要"这个"指针,所以你可以将它传递给c风格函数,就像@jpo38所说的那样。

  • thunk
什么是thunk?你可以在WTL的源代码中找到它。结论是,你可以使用thunk pack"这个"指针。这是一个案例(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;
}