回调非静态方法

Prn*_*nbs 2 c++ opengl glut

想想你的基本GLUT程序.它们只是从main方法运行并包含回调,如`glutMouseFunc(MouseButton),其中MouseButton是方法的名称.

我所做的是将主文件封装到一个类中,这样MouseButton不再是一个静态函数,而是一个实例.但这样做会给我一个编译错误:

错误2错误C3867:'StartHand :: MouseButton':函数调用缺少参数列表; 使用'&StartHand :: MouseButton'创建指向成员c:\ users\angeleyes\documents\visual studio 2008\projects\capstone ver 4\starthand.cpp的指针388 IK Engine

由于类非常庞大,因此无法提供代码示例.

我尝试过使用this->MouseButton但是会出现同样的错误.不能为回调提供指向实例函数的指针吗?

小智 5

正如错误消息所示,您必须使用&StartHand::MouseButton语法来获取指向成员函数的指针(ptmf); 这只是作为语言的一部分.

当使用ptmf时,你正在调用的函数,在这种情况下为glutMouseFunc,也必须期望获得一个ptmf作为回调,否则使用你的非静态MouseButton将无法工作.相反,一种常见的技术是回调与用户提供的void*上下文一起工作,该上下文可以是实例指针 - 但执行回调的库必须明确允许此参数.确保匹配外部库所期望的ABI(下面的handle_mouse函数)也很重要.

由于供应过剩不会允许用户供应的场景,你必须使用另一种机制:你的对象有过剩的当前窗口关联.然而,它确实提供了获取"当前窗口"的方法,并且我已经使用它来void*将窗口关联起来.然后你只需要创建一个trampoline来进行类型转换并调用方法.

机械:

#include <map>

int glutGetWindow() { return 0; } // make this example compile and run  ##E##

typedef std::pair<void*, void (*)(void*,int,int,int,int)> MouseCallback;
typedef std::map<int, MouseCallback> MouseCallbacks;
MouseCallbacks mouse_callbacks;
extern "C" void handle_mouse(int button, int state, int x, int y) {
  MouseCallbacks::iterator i = mouse_callbacks.find(glutGetWindow());
  if (i != mouse_callbacks.end()) { // should always be true, but possibly not
                                    // if deregistering and events arrive
    i->second.second(i->second.first, button, state, x, y);
  }
}

void set_mousefunc(
  MouseCallback::first_type obj,
  MouseCallback::second_type f
) {
  assert(obj); // preconditions
  assert(f);
  mouse_callbacks[glutGetWindow()] = MouseCallback(obj, f);
  //glutMouseFunc(handle_mouse); // uncomment in non-example  ##E##
  handle_mouse(0, 0, 0, 0); // pretend it's triggered immediately  ##E##
}

void unset_mousefunc() {
  MouseCallbacks::iterator i = mouse_callbacks.find(glutGetWindow());
  if (i != mouse_callbacks.end()) {
    mouse_callbacks.erase(i);
    //glutMouseFunc(0); // uncomment in non-example  ##E##
  }
}
Run Code Online (Sandbox Code Playgroud)

例:

#include <iostream>

struct Example {
  void MouseButton(int button, int state, int x, int y) {
    std::cout << "callback\n";
  }
  static void MouseButtonCallback(
    void* self, int button, int state, int x, int y
  ) {
    static_cast<Example*>(self)->MouseButton(button, state, x, y);
  }
};

int main() {
  Example obj;
  set_mousefunc(&obj, &Example::MouseButtonCallback);

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

请注意,您不再直接调用glutMouseFunc ; 它作为[un] set_mousefunc的一部分进行管理.


以防万一不清楚:我已经重写了这个答案,所以它应该对你有用,这样就避免了C/C++链接问题的争论.它将按原样编译和运行(没有过剩),它应该只有很小的修改过剩:评论或取消注释标记的4行##E##.

  • 您不应该使用静态函数作为回调.从技术上讲,该库正在期待一个C函数.静态成员方法不是C函数,您只是很幸运,您的编译器对两者使用相同的ABI.没有要求它这样做.只是为了研究它,使用高度优化的ABI(并在寄存器而不是在堆栈中传递参数)的更昂贵的编译器将破坏以下代码. (6认同)