如何使用函数对象作为回调来调用对象中的成员函数(处理程序)?
How to use function object as callback to invoke member functions (handlers) in an object?
我想创建一个可以在 glfwSet[...]Callback()
函数中使用的 window_callback
函数对象。它应该通过其 GLFWwindow* handle
在 inline static std::unordered_map<GLFWwindow*, window&> windows_by_handles
中找到 window
,然后调用其适当的处理程序。这是我当前的代码:
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
class 内):
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
class 构造函数中):
glfwSetFramebufferSizeCallback(handle, resize_callback);
glfwSetCursorPosCallback(handle, mouse_movement_callback);
glfwSetScrollCallback(handle, scroll_callback);
给定的代码无法编译。在 Visual Studio 2019(使用 C++17)中编译时出现以下错误。前 2 个错误发生在用户定义的转换运算符声明中:
- C2833 'operator type' 不是可识别的运算符或类型
C2059 语法错误:'newline'
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
类型到适当的函数指针类型的转换。应该更改哪些内容才能使此代码正常工作,或者以相同方式工作的替代解决方案是什么?
How to use function object as callback to invoke member functions (handlers) in an object?
that I could use in glfwSet[...]Callback() functions.
Glfw 不接受成员函数指针作为回调函数,也不接受函数对象。
您只能注册函数指针。函数指针不能指向 non-static 成员函数。函数对象的函数调用运算符重载也是成员函数
您需要编写一个 non-member 函数(或者您可以根据需要使用静态成员函数)来包装对成员函数(或者函数对象,如果您愿意)的调用。
示例:
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);
但是,大概您需要访问 class(或函数对象)的某个特定实例,而不是默认构造的实例。毕竟,如果不是这种情况,为什么要使用 non-static 成员函数。为此,您需要通过某种方式将对该对象的引用传递到回调中。
GLFW记录的方式是:
Each window has a user pointer that can be set with glfwSetWindowUserPointer
and queried with glfwGetWindowUserPointer
. This can be used for any purpose you need and will not be modified by GLFW throughout the life-time of the window.
如果要在单个对象上调用所有回调,使用它很简单:
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 似乎不支持回调特定的用户数据。如果不同的回调需要不同的对象,您可以使用自己的从回调到对象的映射,但这并不像上面的示例那么简单。
此外,至少在 window 处于活动状态时要非常小心地保持对象处于活动状态,以免在悬空指针上调用回调。
我想创建一个可以在 glfwSet[...]Callback()
函数中使用的 window_callback
函数对象。它应该通过其 GLFWwindow* handle
在 inline static std::unordered_map<GLFWwindow*, window&> windows_by_handles
中找到 window
,然后调用其适当的处理程序。这是我当前的代码:
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
class 内):
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
class 构造函数中):
glfwSetFramebufferSizeCallback(handle, resize_callback);
glfwSetCursorPosCallback(handle, mouse_movement_callback);
glfwSetScrollCallback(handle, scroll_callback);
给定的代码无法编译。在 Visual Studio 2019(使用 C++17)中编译时出现以下错误。前 2 个错误发生在用户定义的转换运算符声明中:
- C2833 'operator type' 不是可识别的运算符或类型
C2059 语法错误:'newline'
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
类型到适当的函数指针类型的转换。应该更改哪些内容才能使此代码正常工作,或者以相同方式工作的替代解决方案是什么?
How to use function object as callback to invoke member functions (handlers) in an object?
that I could use in glfwSet[...]Callback() functions.
Glfw 不接受成员函数指针作为回调函数,也不接受函数对象。
您只能注册函数指针。函数指针不能指向 non-static 成员函数。函数对象的函数调用运算符重载也是成员函数
您需要编写一个 non-member 函数(或者您可以根据需要使用静态成员函数)来包装对成员函数(或者函数对象,如果您愿意)的调用。
示例:
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);
但是,大概您需要访问 class(或函数对象)的某个特定实例,而不是默认构造的实例。毕竟,如果不是这种情况,为什么要使用 non-static 成员函数。为此,您需要通过某种方式将对该对象的引用传递到回调中。
GLFW记录的方式是:
Each window has a user pointer that can be set with
glfwSetWindowUserPointer
and queried withglfwGetWindowUserPointer
. This can be used for any purpose you need and will not be modified by GLFW throughout the life-time of the window.
如果要在单个对象上调用所有回调,使用它很简单:
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 似乎不支持回调特定的用户数据。如果不同的回调需要不同的对象,您可以使用自己的从回调到对象的映射,但这并不像上面的示例那么简单。
此外,至少在 window 处于活动状态时要非常小心地保持对象处于活动状态,以免在悬空指针上调用回调。