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

时间:2014-11-18 11:08:30

标签: c++

typedef void (*CALLBACK)();
class Filter
{
public:
     void callback()
    {
        cout << "callback" << endl;
    }
};

void SetCallback(CALLBACK pCallBack )
{
    pCallBack();
}



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

主要是SetCallback(f.callback);声明给出了错误。任何人都可以帮我解决问题

4 个答案:

答案 0 :(得分:7)

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

如果遇到需要普通回调函数且没有用户上下文参数(函数只传递给回调的void*)的遗留C接口,则会出现问题。

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

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的(因为你需要生成每个的正确代码,并且存在管理存根的内存的问题。另一方面,有时它是唯一可行的。 Delphi有时为它的窗口和钩子程序做这种事情,ATL也这样做。

答案 1 :(得分:3)

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

可能需要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;
}

答案 2 :(得分:1)

关于“非静态方法”的回调的简单示例:

#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

答案 3 :(得分:0)

您应该考虑回调的真正含义以及如何调用成员函数。

当你给出一个回调函数时,你只需要给出一个函数的地址,该函数稍后会被你通常无法控制的参数调用。

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

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