如何使用函数对象作为回调来调用对象中的成员函数(处理程序)?

时间:2019-07-10 13:44:14

标签: c++ compiler-errors glfw

我想创建一个window_callback函数中可以使用的函数对象。应该通过glfwSet[...]Callback()window中找到一个inline static std::unordered_map<GLFWwindow*, window&> windows_by_handles,然后调用其适当的处理程序。这是我当前的代码:

GLFWwindow* handle

template <typename... Args> class window_callback { typedef void(window::*member_func_t)(Args...); member_func_t func; public: window_callback(member_func_t func_) : func(func_) { } void operator() (GLFWwindow* HWND, Args... args) { try { window& wnd = windows_by_handles.at(HWND); (wnd.*func)(args...); //error E0165 } catch (const std::out_of_range&) { std::cerr << "Detected callback for unknown window\n"; } } operator decltype(&(operator()))() { return &operator(); } }; 对象的创建方式如下(在window_callback类内部):

window

然后,它们与inline static window_callback<int, int> resize_callback{ &on_resize }; inline static window_callback<double, double> mouse_movement_callback{ &on_mouse_moved }; inline static window_callback<double, double> scroll_callback{ &on_scrolled }; 这样的函数绑定(在glfwSet[...]Callback()类构造函数内部):

window

给定的代码无法编译。在Visual Studio 2019(使用C ++ 17)中进行编译时,它会出现以下错误。前两个错误发生在用户定义的转换运算符声明上:

  • C2833'运算符类型'不是公认的运算符或类型
  • C2059语法错误:'newline'

  • C2664 glfwSetFramebufferSizeCallback(handle, resize_callback); glfwSetCursorPosCallback(handle, mouse_movement_callback); glfwSetScrollCallback(handle, scroll_callback); :无法将参数2从GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow *,GLFWframebuffersizefun)转换为window::window_callback<int,int>

  • C2664 GLFWframebuffersizefun:无法将参数2从GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow *,GLFWcursorposfun)转换为window::window_callback<double,double>
  • C2664 GLFWcursorposfun:无法将参数2从GLFWscrollfun glfwSetScrollCallback(GLFWwindow *,GLFWscrollfun)转换为window::window_callback<double,double>

有问题的部分是从GLFWscrollfun类型到适当的函数指针类型的转换。为使此代码正常工作应进行哪些更改,或者以相同方式工作的替代解决方案是什么?

1 个答案:

答案 0 :(得分:2)

  

如何使用函数对象作为回调来调用对象中的成员函数(处理程序)?

     我可以在glfwSet [...] Callback()函数中使用的

Glfw不接受成员函数指针作为回调函数,也不接受函数对象。

您只能注册函数指针。函数指针不能指向非静态成员函数。函数对象的函数调用操作符重载也是成员函数。

您需要编写一个非成员函数(或者如果愿意,可以使用静态成员函数)来包装对成员函数(或函数对象)的调用。

示例:

glfwSetWindowUserPointer

但是,大概您需要访问类(或函数对象)的某些特定实例,而不是默认构造的实例。毕竟,如果不是这样,为什么要使用非静态成员函数。为此,您需要某种方式将对该对象的引用传递到回调中。

GLFW记录的方式是:

  

每个窗口都有一个用户指针,可以使用glfwGetWindowUserPointer进行设置,并可以使用C c; glfwSetWindowUserPointer(window, &c); auto window_size_callback = [](GLFWwindow* window, int w, int h) { C* cptr = static_cast<C*>(glfwSetWindowUserPointer(window)); assert(c); cptr->window_size(window, w, h); }; 进行查询。此功能可用于您需要的任何目的,并且不会在窗口的整个使用期内被GLFW修改。

如果要在单个对象上调用所有回调,则使用此方法很简单:

-----------------------------------------------------------------
                 | Article 1 | Article 2 | Article 3 | Article 4 |
Article 0        |                                               |
                 | Article 5 | Article 6 | Article 7 | Article 8 |
-----------------------------------------------------------------

不幸的是,GLFW API似乎不支持特定于回调的用户数据。如果您需要不同的对象用于不同的回调,则可以使用自己的从回调到对象的映射,但这并不像上面的示例那么简单。

此外,要非常小心,至少在窗口处于活动状态时使该对象保持活动状态,以免在悬空的指针上调用回调。