【问题标题】:C++ Function Callbacks: Cannot convert from a member function to a function signatureC++ 函数回调:无法从成员函数转换为函数签名
【发布时间】:2018-04-14 07:36:19
【问题描述】:

我正在使用第 3 方库,它允许我为某些事件注册回调。注册函数看起来像这样。它使用回调签名。

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'

我一直在研究诸如 function 和 bind 之类的 boost 库,但似乎没有一个能够解决问题。我一直在谷歌上搜索答案,但我什至不知道该怎么称呼它,所以没有太大帮助。

提前致谢。

【问题讨论】:

  • 谢谢大家!我发现另一篇提出同样问题的帖子,看起来共识只是使成员函数成为普通函数或使其成为静态函数。 link。除非有人有一些非常疯狂的 TMP 技巧?

标签: c++ function boost bind signature


【解决方案1】:

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

你可以:

  1. 更改参数的类型以接受成员函数并接受实例作为调用对象
  2. 停止尝试传递成员函数并传递普通函数(也许通过将函数设为static
  3. 有一个普通函数,它接受一个类的实例、一个成员函数指针和一个 std::string,并使用类似 boost 的 bind 来绑定前两个参数
  4. 让回调注册函数接受一个仿函数对象,或者一个std::function(我想是这个名字)
  5. 还有许多其他方法,我不会在此详述,但您会明白的。

【讨论】:

  • 问题是我无法更改名为registerCallback 的函数。它是第三方库的一部分。我不能使用boost::bind,因为我遇到了类似的错误。 boost::bind 返回一个 bind_t 对象,我相信它是某种函子。在任何情况下,它都不是函数指针,编译器会抱怨它无法将 bind_t 转换为 Callback
  • 如果它对我所说的第 3 方库有帮助的话,那就是 V8 引擎。我想要做的具体任务是注册一个 JavaScript 回调函数,像这样 ObjectTemplate::New()->Set(String::New("getRemote"), FunctionTemplate::New(&RemoteObject::getRemoteCallback));FunctionTemplate::New 需要一个 InvokationCallback,看起来像这样 typedef Handle<Value> (*InvocationCallback)(const Arguments& args);
【解决方案2】:

成员函数不能转换为its own good reasons 的普通函数。如果您的设计允许,则将 MyStruct::myCallback() 设为 static 成员方法,代码应该可以正常工作。

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

【讨论】:

    【解决方案3】:
    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  called\n";
        }
    };
    
    void FireAllCallBack(std::string& str )
    {
        for ( std::set<ICallBackInterface*>::iterator iter = CallBackMgr.begin();
            iter != CallBackMgr.end();
            ++iter)
        {
            (*iter)->FireCallBack( str );
        }
    }
    

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

    【讨论】:

      【解决方案4】:

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

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

      有关使用它的提示,请参阅this SO question。

      【讨论】:

      • 因为它们不再兼容。
      【解决方案5】:

      取决于你如何使用它......例如,单顿会更简单

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

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

      【讨论】:

      • 我想注册一个成员函数的原因是我可以存储回调在调用时可以使用的附加数据,并且将其封装在一个类中会很好。如果我使用单例或使我的类全部静态,我将不得不使用某种映射来存储和检索我想要的数据,基于我从参数中获得的微薄数据量,在很多情况下是不保证是唯一的。
      • 如果字符串中没有任何东西可以说明它应该去哪个对象,我相信使用更适合的库或修改它会比绕过它更好。 y您可以查看 ATL thunking。基本上,您需要在运行时为每个对象生成一个调用您的成员函数的函数。使用更新的标准和库可能会更容易。
      【解决方案6】:

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

      如果您只需要设置单个回调,一个选项是拥有一个引用单例实例指针的静态或命名空间私有函数,并在回调时使用它来调度。

      如果您需要多个项目,那么模板可能会包含这些 hackiness(此处未经测试的代码,只是想法)。

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

      【讨论】:

      • 很有想象力!不幸的是,我需要在运行时动态注册这些回调。如果我提前知道我需要注册的所有回调,这将起作用。也许我会看一下 Chrome 代码库,看看他们做了什么。 V8 文档非常缺乏:(
      猜你喜欢
      • 2011-06-17
      • 2010-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多