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

How to use function object as callback to invoke member functions (handlers) in an object?

我想创建一个可以在 glfwSet[...]Callback() 函数中使用的 window_callback 函数对象。它应该通过其 GLFWwindow* handleinline 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 个错误发生在用户定义的转换运算符声明中:

有问题的部分是从 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 处于活动状态时要非常小心地保持对象处于活动状态,以免在悬空指针上调用回调。