c++ - 如何使用函数对象作为回调来调用对象中的成员函数(处理程序)?
问题描述
我想创建一个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
类构造函数内部):
glfwSetFramebufferSizeCallback(handle, resize_callback);
glfwSetCursorPosCallback(handle, mouse_movement_callback);
glfwSetScrollCallback(handle, scroll_callback);
给定的代码无法编译。在 Visual Studio 2019(使用 C++17)中编译时出现以下错误。前 2 个错误发生在用户定义的转换运算符声明中:
- C2833 '运算符类型' 不是可识别的运算符或类型
C2059 语法错误:“换行符”
C2664
GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow *,GLFWframebuffersizefun)
: 无法将参数 2 从转换window::window_callback<int,int>
为GLFWframebuffersizefun
- C2664
GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow *,GLFWcursorposfun)
: 无法将参数 2 从转换window::window_callback<double,double>
为GLFWcursorposfun
- C2664
GLFWscrollfun glfwSetScrollCallback(GLFWwindow *,GLFWscrollfun)
: 无法将参数 2 从转换window::window_callback<double,double>
为GLFWscrollfun
有问题的部分是从window_callback
类型到适当的函数指针类型的转换。应该进行哪些更改才能使此代码正常工作,或者以相同方式工作的替代解决方案是什么?
解决方案
如何使用函数对象作为回调来调用对象中的成员函数(处理程序)?
我可以在 glfwSet[...]Callback() 函数中使用。
glfw 不接受成员函数指针作为回调函数,也不接受函数对象。
您只能注册函数指针。函数指针不能指向非静态成员函数。函数对象的函数调用运算符重载也是成员函数。
您需要编写一个非成员函数(或者,如果您愿意,可以使用静态成员函数)来包装对成员函数(或函数对象,如果您愿意)的调用。
例子:
struct C {
void window_size(GLFWwindow*, int, int);
};
auto window_size_callback = [](GLFWwindow* window, int w, int h) {
C c;
c.window_size(window, w, h);
}; // a non-capturing lambda can be converted to a function pointer
// you can use a named function if you prefer
glfwSetWindowSizeCallback(window, window_size_callback);
但是,大概您需要访问类(或函数对象)的某些特定实例,而不是默认构造的实例。毕竟,如果不是这样,为什么要使用非静态成员函数。为此,您需要某种方式将对该对象的引用传递到回调中。
GLFW 记录的方式是:
每个窗口都有一个用户指针,可以用 设置
glfwSetWindowUserPointer
和查询glfwGetWindowUserPointer
。这可以用于您需要的任何目的,并且在窗口的整个生命周期内都不会被 GLFW 修改。
如果要在单个对象上调用所有回调,则使用它很简单:
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 API 似乎不支持回调特定的用户数据。如果您需要为不同的回调使用不同的对象,您可以使用自己的从回调到对象的映射,但这并不像上面的示例那么简单。
此外,至少只要窗口处于活动状态,请非常小心地保持对象处于活动状态,以免在悬空指针上调用回调。
推荐阅读
- c# - 从 TCP 服务器 C# 将数据发送到文本文件
- docker - 是否可以从 docker-compose 为容器设置 UID?
- python - TF map_fn 太慢了
- javascript - 检查元素是否在 cypressIO 中按字母顺序排序
- java - 休眠连接中的多对一
- c# - asp.net core web api中的AppMetrics健康检查
- c# - 加载后如何让 ComboBox 显示 DisplayMember?
- excel - 从新的电子表格和格式中跨多个选项卡检索相同的设置单元格值
- jmeter - JMeter 5.0 中的测试动作采样器
- java - JavaFX - 编辑 TableView 不会更新模型属性