简体   繁体   English

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

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

I wanted to create a window_callback function object that I could use in glfwSet[...]Callback() functions. 我想创建一个可以在glfwSet[...]Callback()函数中使用的window_callback函数对象。 It is supposed to find a window in the inline static std::unordered_map<GLFWwindow*, window&> windows_by_handles by its GLFWwindow* handle and than call its appropriate handler. 应该通过inline static std::unordered_map<GLFWwindow*, window&> windows_by_handles GLFWwindow* handle找到一个window ,然后调用其适当的处理程序。 This is my current code: 这是我当前的代码:

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(); }
};

The window_callback objects are created in the following manner (inside the window class): 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 };

Then, they are bound with glfwSet[...]Callback() functions like this (inside the window class constructor): 然后,将它们与glfwSet[...]Callback()函数绑定在一起(在window类构造函数内部):

glfwSetFramebufferSizeCallback(handle, resize_callback);
glfwSetCursorPosCallback(handle, mouse_movement_callback);
glfwSetScrollCallback(handle, scroll_callback);

The given code doesn't compile. 给定的代码无法编译。 It gives the following errors when compiling in Visual Studio 2019 (using C++17). 在Visual Studio 2019(使用C ++ 17)中进行编译时,它会出现以下错误。 The first 2 errors happen on the user-defined conversion operator declaration: 前两个错误发生在用户定义的转换运算符声明上:

  • C2833 'operator type' is not a recognized operator or type C2833'运算符类型'不是公认的运算符或类型
  • C2059 syntax error: 'newline' C2059语法错误:“换行符”

  • C2664 GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow *,GLFWframebuffersizefun) : cannot convert argument 2 from window::window_callback<int,int> to GLFWframebuffersizefun C2664 GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow *,GLFWframebuffersizefun) :无法将参数2从window::window_callback<int,int>GLFWframebuffersizefun

  • C2664 GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow *,GLFWcursorposfun) : cannot convert argument 2 from window::window_callback<double,double> to GLFWcursorposfun C2664 GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow *,GLFWcursorposfun) :无法将参数2从window::window_callback<double,double>GLFWcursorposfun
  • C2664 GLFWscrollfun glfwSetScrollCallback(GLFWwindow *,GLFWscrollfun) : cannot convert argument 2 from window::window_callback<double,double> to GLFWscrollfun C2664 GLFWscrollfun glfwSetScrollCallback(GLFWwindow *,GLFWscrollfun) :无法将参数2从window::window_callback<double,double>GLFWscrollfun

The problematic part is the conversion from the window_callback type to the appropriate function pointer types. 问题部分是从window_callback类型到适当的函数指针类型的转换。 What should be changed to make this code work or what is an alternate solution that will work in the same manner? 为了使此代码有效,应进行哪些更改?或者将以相同的方式起作用的替代解决方案是什么?

How to use function object as callback to invoke member functions (handlers) in an object? 如何使用函数对象作为回调来调用对象中的成员函数(处理程序)?

that I could use in glfwSet[...]Callback() functions. 我可以在glfwSet [...] Callback()函数中使用。

Glfw does not accept member function pointers as callback functions, nor does it accept function objects. Glfw不接受成员函数指针作为回调函数,也不接受函数对象。

You can only register function pointers. 您只能注册函数指针。 Function pointers cannot point to non-static member functions. 函数指针不能指向非静态成员函数。 The function call operator overload of a function object is also a member function. 函数对象的函数调用操作符重载也是成员函数。

You need to write a non-member function (or you can use a static member function if you prefer) that wraps the call to the member function (or a function object if you prefer). 您需要编写一个非成员函数(或者,如果愿意,可以使用静态成员函数)来包装对成员函数(或函数对象)的调用。

Example: 例:

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);

But, presumably you need access to some particular instance of the class (or function object), rather than a default constructed one. 但是,大概您需要访问类(或函数对象)的某些特定实例,而不是默认构造的实例。 After all, why use a non-static member function if that wasn't the case. 毕竟,如果不是这样,为什么要使用非静态成员函数。 To achieve that, you need some way of passing a reference to that object into the callback. 为此,您需要某种方式将对该对象的引用传递到回调中。

The way documented by GLFW is: GLFW记录的方式是:

Each window has a user pointer that can be set with glfwSetWindowUserPointer and queried with glfwGetWindowUserPointer . 每个窗口具有可与设置用户指针glfwSetWindowUserPointer并用查询glfwGetWindowUserPointer This can be used for any purpose you need and will not be modified by GLFW throughout the life-time of the window. 它可以用于您需要的任何目的,并且不会在窗口的整个使用期内被GLFW修改。

If all of your callbacks are to be called on a single object, using this is simple: 如果要在单个对象上调用所有回调,则使用此方法很简单:

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);
};

Unfortunately, GLFW API doesn't appear to support callback specific user data. 不幸的是,GLFW API似乎不支持特定于回调的用户数据。 If you need different objects for different callbacks, you could possibly use your own map from callback to object, but that's not quite as simple as the example above. 如果不同的回调需要不同的对象,则可以使用自己的从回调到对象的映射,但这并不像上面的示例那么简单。

Furthermore, be very careful to keep the object alive at least as long as the window is alive so that callbacks don't get called on a dangling pointer. 此外,要非常小心,至少在窗口处于活动状态时使该对象保持活动状态,以免在悬空的指针上调用回调。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM