设置一个回调函数,该函数是类的非静态成员函数

Setting a callback function which is non static member function of a class

本文关键字:函数 静态成员 设置 一个 回调      更新时间:2023-10-16
typedef void (*CALLBACK)();
class Filter
{
public:
     void callback()
    {
        cout << "callback" << endl;
    }
};
void SetCallback(CALLBACK pCallBack )
{
    pCallBack();
}

int main()
{
    Filter f;
    SetCallback(f.callback);
}

在main中,SetCallback(f.callback); 语句给出错误。谁能帮我解决问题

问题是成员函数不是没有参数的普通函数,因为它始终具有隐式this参数。

如果您遇到一个传统的 C 接口,该接口需要一个没有用户上下文参数的普通回调函数(函数只是传递给回调的void*),那么您就有问题了。

如果您确实有用户上下文,则很容易。将对象指针作为上下文传递,并使用包装函数作为实际回调:

typedef void (*CALLBACK)(void*);
class Filter
{
public:
    static void CallbackWrapper(void* context) {
        static_cast<Filter*>(context)->callback();
    }
private:
    void callback();
};
int main() {
    Filter f;
    SetCallback(&Filter::CallbackWrapper, &f);
}

如果您没有上下文,以下是一些选项:

  • 将对象存储在全局变量中,并从包装器访问它。这有一个明显的缺点,即使用全局变量,并且不允许以这种方式进行多个回调。对于长时间运行的回调,这真的很糟糕。
  • 对上述内容的一个小改进是使用线程局部全局变量。这对于严格范围的回调很有趣,例如,您调用一个函数,该函数将立即多次使用您的回调,然后返回。想qsort().至少这样,您不会遇到线程安全问题。仍然不是长时间运行的回调的选项。
  • 最后,在
  • 大多数平台上都可以使用但工作量很大的选项,您可以在运行时生成一个嵌入对象指针的存根函数。这基本上意味着分配一块内存,如果平台使用它,则禁用该内存上的执行保护,并将机器代码放在那里加载对象指针并在其上调用函数。

最后一个选项仍然有很多缺点:它非常特定于平台,甚至可能在某些平台上根本不起作用(您无法在 iOS、AFAIK 中禁用执行保护),它是特定于 CPU 的(因为您需要为每个生成正确的代码),并且存在管理存根内存的问题。另一方面,有时它是唯一有效的方法。德尔福有时会为其窗口和钩子程序做这种事情,ATL 也会这样做。

这是我用来实现指向成员函数的指针的回调的方法。

它可能需要C++11。

#include <iostream>
#include <string>
#include <functional>
using namespace std;
struct MessageSource
{
       function<void(const string& msg)> _callback;
       template<typename A, typename B>
       void connect(A func_ptr, B obj_ptr)
       {
              _callback = bind(func_ptr, obj_ptr, placeholders::_1);
       }
       void send_msg(const string& msg)
       {
              if (_callback)
                     _callback(msg);
       }
       void disconnect()
       {
              _callback = nullptr;
       }
};
struct Printer
{
       void print(const string& msg) { std::cout << msg << std::endl; };
};
int main()
{
       {
              Printer p;
              MessageSource s;
              s.connect(&Printer::print, &p);
              s.send_msg("test");
              s.disconnect();
              s.send_msg("test again");
       }
       system("pause");
       return 0;
}

关于"非静态方法"回调的更简单示例:

#include <iostream>
#include <string>
#include <functional>
using namespace std::placeholders;
class Test
{
public:
    void SetValue(int i) { v = i;}
    int v;
};
int main()
{
    Test a { 123 };
    std::cout << a.v << std::endl;  // print 123
    auto _callback = std::bind(&Test::SetValue, &a, _1); // create the callback
    _callback(55); // call the callback
    std::cout << a.v << std::endl; // print 55
    return 0;
}

输出:

123
55

您应该考虑回调到底是什么以及如何调用成员函数。

当你给出一个

回调函数时,你只给出一个函数的地址,该函数稍后将使用你通常几乎无法控制的参数调用。

调用成员函数时,其第一个参数是this指针,即对调用该方法的对象的引用。

这就是为什么不能使用成员方法作为回调的原因。您只能使用不需要特殊参数(对程序员隐含,但在编译器的角度来看是真实的)参数this的 true 函数或静态成员函数。