首页 > 解决方案 > 如何使用函数对象作为回调来调用对象中的成员函数(处理程序)?

问题描述

我想创建一个window_callback可以在函数中使用的函数对象glfwSet[...]Callback()。它应该通过它window在其中找到一个,然后调用它的适当处理程序。这是我当前的代码:inline static std::unordered_map<GLFWwindow*, window&> windows_by_handlesGLFWwindow* 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 个错误发生在用户定义的转换运算符声明中:

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

标签: c++compiler-errorsglfw

解决方案


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

我可以在 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 似乎不支持回调特定的用户数据。如果您需要为不同的回调使用不同的对象,您可以使用自己的从回调到对象的映射,但这并不像上面的示例那么简单。

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


推荐阅读