如何在不相关的类之间传递C ++回调?

时间:2012-08-13 18:30:46

标签: c++ visual-studio-2010 function-pointers

在非升级项目中,我有一个类,它使用基于某个用户操作的计时器(按下/释放按钮)。我希望这个类是通用的,因此需要对用户定义的操作进行回调。

// TimerClass.h
typedef void (*timerCallback)(void);
...
Class TimerClass : public CButton {
public:
    void setCallbackShortTime(timerCallback clickFn) { shortCallback = clickFn;} ;
    void setCallbackLongTime(timerCallback clickFn) { longCallback = clickFn;} ;
private:
    timerCallback shortCallback, longCallback;
}


// CMyDlg.h
class CMyDlg : public CDialog
{
public:
    void openLiveViewWindow();
    void openAdminPanelWindow();
    TimerClass _buttonSettings;
}

// CMyDlg.cpp
...
_buttonSettings.setCallbackShortTime(&openLiveViewWindow);
...

现在,从另一个类(DialogClass)我可以使用TimerClass但是我无法将函数指针传递给回调函数。这些功能不是静态的。编译器最终抱怨:

error C2276: '&' : illegal operation on bound member function expression

对此的一些研究指出std::function()std::bind(),但我对这些并不熟悉,并会对如何解决此问题提出一些指示。

解决方案:对于任何有兴趣的人,以下是最终解决方案的砖块

// TimedButton.h
#include <functional>
// define timer callback prototype
typedef std::function<void()> timerCallback;
...
class TimedButton : public CButton
{
public:
    TimedButton();
    ...
    void setCallbackShortTime(timerCallback clickFn) { _shortCallback = clickFn;} ;
    void setCallbackLongTime(timerCallback clickFn) { _longCallback = clickFn;} ;
private:
    timerCallback _shortCallback;
    timerCallback _longCallback;
}

// TimedButton.cpp
...
(_longCallback)();  // call long duration fn
...
(_shortCallback)();     // call short duration fn

// in MyDlg.cpp
#include <functional>
...
_buttonSettings.setCallbackShortTime(std::bind(&CMyDlg::openLiveViewWindow, this));
_buttonSettings.setCallbackLongTime(std::bind(&CMyDlg::openAdminPanelWindow, this));

4 个答案:

答案 0 :(得分:3)

这种老式的方法是让你的回调函数接受一个额外的void*参数,为此你可以将一个指针传递给你想要调用该函数的对象。然后使用静态成员函数进行回调,让它将指针强制转换为正确的对象并调用真正的回调函数。

typedef void (*timerCallback)(void*);
...
void setCallbackShortTime(timerCallback clickFn, void* object) { shortCallback = clickFn; shortCallbackObject = object;} ;
void setCallbackLongTime(timerCallback clickFn, void* object) { longCallback = clickFn; longCallbackObject = object;} ;
...

static void openLiveViewWindowCallback(void* object) { ((CMyDlg*)object)->openLiveViewWindow(); }
void openLiveViewWindow();

答案 1 :(得分:2)

std::function是一个多态函数对象,它可以包含任何类型的具有特定签名的可调用对象。在您的情况下,您希望它不带参数并且不返回任何值,因此您可以定义:

typedef std::function<void()> timerCallback;

std::bind允许您通过将参数绑定到参数来使可调用对象适应其中一个不同的签名。在您的情况下,您希望通过将成员函数绑定到特定对象来调整它来调整它:

_buttonSettings.setCallbackShortTime(std::bind(&CMyDlg::openLiveViewWindow, this));

请注意,这些是在2011年推出的,因此较旧的编译器不支持它们。在这种情况下,您可以使用非常相似的Boost或TR1库,也可以创建自己的可调用类,其中包含指向成员函数的指针和指向要调用它的对象的指针/引用。

答案 2 :(得分:2)

您不能将指针传递给类的方法,只能传递普通函数。我建议深入研究std::function(),因为你正在使用支持它们的VS2010。有一个很好的(和很长的)教程描述了它们以及更多here

答案 3 :(得分:1)

您可以创建一个充当函数指针的多态模板类。

class TFunctorBase
{
public:
    TFunctorBase(void) {}
    virtual ~TFunctorBase(void) {}
    virtual void operator()(void) = 0;
};

// derived template class
template <class TClass> class TFunctor : public TFunctorBase
{
private:
    void (TClass::*fpt)();   // pointer to member function
    TClass* pt2Object;                  // pointer to object

public:
    TFunctor(TClass* _pt2Object, void(TClass::*_fpt)())
    { pt2Object = _pt2Object; fpt = _fpt;};

    virtual void operator()(void)
    { (*pt2Object.*fpt)();};              // execute member function
};

初始化仿函数对象:

TFunctor<MyClass> obj(&myInstance, &MyClass::myMemberFunction);

使用它:

(*obj)(); 
//(*obj)(42); for operator()(int)

以下是一个例子:

class ClassA
{
public:
   void function1(int a, string b);
};

ClassA objA;
TFunctor<ClassA> functor(&objA,&ClassA::function);
(*functor)(42, "pumpkin"); //assuming you added virtual void operator()(int, string) to TFunctorBase

这是关于仿函数的一个很好的资源以及我上面描述的实现。 http://www.newty.de/fpt/functor.html#chapter4