C++函数回调:无法从成员函数转换为函数签名

C++ Function Callbacks: Cannot convert from a member function to a function signature

本文关键字:函数 转换 成员 回调 C++      更新时间:2023-10-16

我使用的是第三方库,它允许我注册某些事件的回调。register函数看起来像这样。它使用回调签名。

typedef int (*Callback)(std::string);
void registerCallback(Callback pCallback) {
//it gets registered
}

我的问题是,我想注册一个成员函数作为回调,类似于

struct MyStruct {
    MyStruct();
    int myCallback(std::string str);
};
MyStruct::MyStruct() {
    registerCallback(&MyStruct::myCallback);
}
int MyStruct::myCallback(std::string str) {
    return 0;
}

当然,编译器抱怨说

错误C2664:"registerCallback":无法将参数1从"int(__thiscall MyStruct::*((std::string("转换为"Callback">

我一直在研究诸如函数和绑定之类的boost库,但似乎没有一个能够做到这一点。我一直在谷歌上搜索答案,但我甚至不知道该怎么称呼,所以这并没有多大帮助。

提前谢谢。

您试图将成员函数指针作为普通函数指针传递,但这是不起作用的。成员函数必须将this指针作为隐藏参数之一,而普通函数则不然,因此它们的类型是不兼容的。

您可以:

  1. 更改参数的类型以接受成员函数,并接受实例作为调用对象
  2. 停止尝试传递成员函数并传递正常函数(可能通过使函数static(
  3. 有一个普通函数,它接受类的一个实例、一个成员函数指针和一个std::string,并使用类似boost的bind之类的东西来绑定前两个参数
  4. 让回调注册函数接受一个functor对象或std::function(我想这就是名称(
  5. 许多其他方式,我不会在这里详细介绍,但你会明白的

成员函数不能转换为正常函数,这是有充分理由的。如果您的设计允许,那么将MyStruct::myCallback()作为static成员方法,代码应该可以正常工作。

struct MyStruct {
  ...
  static int myCallback(std::string str);
  ^^^^^^
};

由于回调的实际函数类型的硬编码不灵活,您无法使用任何常规的调整或绑定技巧来帮助您。第三方库确实希望您传入一个非成员函数,但您无能为力。您可能需要考虑为什么要将成员函数的地址传递给它,以及您对库的设计或使用是否会发生变化。

如果您只需要设置一个回调,那么一种选择是使用一个静态或命名空间私有函数,该函数引用一个单例实例指针,并在回调时使用该指针进行调度。

如果你需要多个项目,那么可能会有一个模板来包装这些技巧(这里是未经测试的代码,只是想法(。

template <int which_callback>
struct CallbackHolderHack
{
    static int callback_func(std::string str) { dispatchee_->myCallback(str); }
    static MyStruct* dispatchee_;
};
template <int which_callback>
MyStruct* CallbackHolderHack::dispatchee_(0);

并使用它:

CallbackHolderHack<0>::dispatchee_ = new MyStruct;
registerCallback(&CallbackHolderHack<0>::callback_func);

取决于您如何使用它……例如,Singlton会简化

struct MyStruct {
    static MyStruct& Create() { 
        static MyStruct m; return m;
    }
    static int StaticCallBack(std::string str) { 
        return Create().Callback(str)
    }
    private:
    int CallBack(std::string str);
    MyStruct();
};

或者,如果你想拥有许多这样的对象,你有几个选择。在调用回调之前,您需要一种方法来路由它。

既然您使用的是C++,为什么不将回调设置为函子对象呢?然后您可以使用std::mem_fun

编辑:似乎std::mem_fun在最新的C++11标准中已被弃用。因此,如果您有一个新的编译器,std::function可能是一个更好的解决方案。

有关使用它的提示,请参阅此SO问题。

class ICallBackInterface
{
public:
    virtual void FireCallBack( std::string& str ) = 0;
};
std::set<ICallBackInterface*> CallBackMgr;
class MyStruct : public ICallBackInterface
{
public:
    MyStruct()
    {
        CallBackMgr.insert( this );
    }
    ~MyStruct()
    {
        CallBackMgr.erase( this );
    }
    virtual void FireCallBack( std::string& str )
    {
        std::cout << "MyStruct  calledn";
    }
};
void FireAllCallBack(std::string& str )
{
    for ( std::set<ICallBackInterface*>::iterator iter = CallBackMgr.begin();
        iter != CallBackMgr.end();
        ++iter)
    {
        (*iter)->FireCallBack( str );
    }
}

这是使用多态性来实现相同效果的另一种方法