C++ 如何通过c++;类函数作为回调函数

C++ 如何通过c++;类函数作为回调函数,c++,C++,我在理解如何将其传递给传递给库的回调函数时遇到了一些困难。函数需要有一个特定的签名 正在讨论的库是OCILIB(),它试图将一个类函数作为Register()的第四个参数传递 我可以毫无问题地通过下面的考试 &数据库::回调//数据库的静态函数 或 [](ocilib::Event&Event)//作为lambda函数 { } 但它不能访问实例变量。我试着像这样使用它 [&](ocilib::Event&Event)//作为lambda函数 { } 但是签名不匹配,我得到以下错误 data

我在理解如何将其传递给传递给库的回调函数时遇到了一些困难。函数需要有一个特定的签名

正在讨论的库是OCILIB(),它试图将一个类函数作为Register()的第四个参数传递

我可以毫无问题地通过下面的考试

&数据库::回调//数据库的静态函数

[](ocilib::Event&Event)//作为lambda函数
{
}
但它不能访问实例变量。我试着像这样使用它

[&](ocilib::Event&Event)//作为lambda函数
{
}
但是签名不匹配,我得到以下错误

database.cpp: In member function ‘bool dcn::database::watch(std::__cxx11::string)’:
database.cpp:104:44: error: no matching function for call to ‘ocilib::Subscription::Register(ocilib::Connection&, std::__cxx11::string&, ocilib::Subscription::ChangeTypesValues, dcn::database::watch(std::__cxx11::string)::<lambda(ocilib::Event&)>, unsigned int, unsigned int)’
    }, (unsigned int) 7778, (unsigned int) 0);
                                            ^
In file included from /usr/include/ocilib.hpp:9194:0,
                 from /home/ai/dcn/include/main.h:17,
                 from database.cpp:1:
/usr/include/ocilib_impl.hpp:6650:13: note: candidate: void ocilib::Subscription::Register(const ocilib::Connection&, const ostring&, ocilib::Subscription::ChangeTypes, ocilib::Subscription::NotifyHandlerProc, unsigned int, unsigned int)
 inline void Subscription::Register(const Connection &connection, const ostring& name, ChangeTypes changeTypes, NotifyHandlerProc handler, unsigned int port, unsigned int timeout)
             ^~~~~~~~~~~~
/usr/include/ocilib_impl.hpp:6650:13: note:   no known conversion for argument 4 from ‘dcn::database::watch(std::__cxx11::string)::<lambda(ocilib::Event&)>’ to ‘ocilib::Subscription::NotifyHandlerProc {aka void (*)(ocilib::Event&)}’
make[1]: *** [database.o] Error 1

需要你的帮助来解决这个问题。提前感谢。

这是一个可怕的API。任何一个拥有回调函数而又不要求使用
void*
的人都在编写我们在70年代就知道是个坏主意的代码

您别无选择,只能使用全局状态发回类

template<std::size_t N, class R, class...Args>
struct crappy_api_fix{
  static std::array< std::function<R(Args&&...)>, N >& table(){
    static std::array< std::function<R(Args&&...)>, N > arr;
    return arr;
  }
  template<std::size_t I>
  static R call( Args...args ){ return table()[I]( std::forward<Args>(args)... ); }
  using sig=R(Args...);
  template<std::size_t I=N-1>
  static sig* make(std::function<R(Args&&...)> f){
    if(!table()[I]){
      table()[I]=f;
      return &call<I>;
    }
    if(I==0) return nullptr;
    return make< (I-1)%N >(f);
  }
  template<std::size_t I=N-1>
  static void recycle( sig* f ){
    if (f==call<I>){
      table()[I]={};
      return;
    }
    if (I==0) return;
    recycle< (I-1)%N >( f);
  }
};

抱歉打错了,在我的手机上打这个。

是的-经典问题。OCILIB只允许您为回调注册
void(*)(OCILIB::Event&)
,并且只能与静态成员或独立函数匹配。您需要编写这样一个函数,在内部查询一些记录,以确定在哪个事件上调用成员函数。例如,它可能在回调期间查看
ocilib::Event
参数,然后使用某个id查找关联容器(
map
unordered\u map
),以查找指向要在其上调用非静态成员函数的对象的指针。(如果OcILIB的代码>订阅::登记器< /C++ >在现代C++中被改写,它应该改为<代码> STD::Fuff< /Cal>参数,而不是非成员函数指针参数,这将允许您通过捕获来传递lambda。)如果API不允许您通过一个
void*
,那么如果不采用不理想的解决方案,您将无法做很多事情。您能否给出一些有关此调用的上下文?进行调用的代码是否可以在同一程序执行中多次运行?@chris yes它将被多次调用。回调是钩子的一部分,它将等待数据库中的更改并复制到另一个数据库。我不是指回调本身,而是指注册回调的代码。有不同级别的黑客解决方案,其中一个是生成一个函数指针,如果再次调用注册码,该指针将被覆盖。其他级别是v对更多工作和覆盖的假设较少或较弱的程度进行分析。
template<std::size_t N, class R, class...Args>
struct crappy_api_fix{
  static std::array< std::function<R(Args&&...)>, N >& table(){
    static std::array< std::function<R(Args&&...)>, N > arr;
    return arr;
  }
  template<std::size_t I>
  static R call( Args...args ){ return table()[I]( std::forward<Args>(args)... ); }
  using sig=R(Args...);
  template<std::size_t I=N-1>
  static sig* make(std::function<R(Args&&...)> f){
    if(!table()[I]){
      table()[I]=f;
      return &call<I>;
    }
    if(I==0) return nullptr;
    return make< (I-1)%N >(f);
  }
  template<std::size_t I=N-1>
  static void recycle( sig* f ){
    if (f==call<I>){
      table()[I]={};
      return;
    }
    if (I==0) return;
    recycle< (I-1)%N >( f);
  }
};
// up to 50 callbacks alive at once:
using cb_helper = crappy_api_fix<50, void, ocilib::Event &>;

// "stateless" function pointer wrapping a lambda:
void(*f)(ocilib::Event&) = cb_helper::make([&](ocilib::Event &event) {});

// blah is the API class with the Register method.  We pass the f from above:
blah->Register(arg1, arg2, f, arg4);

// This might be far away fron the above code:
// we should keep copy of the f; when we have unregistered, we shoukd recyle it:
cb_helper::recycle(f); // when `f` is no longer needed