如何将key_callback与包装类实例相关联?


11

我正在尝试将我的GLFW3调用包装到一个类中:

class WindowManager {
private:
    GLFWwindow* window_;
    GLFWmonitor* monitor_;
    Keyboard* keyboard_;
...
}

我正在尝试设置一个单例键盘类,该类收集执行期间的按键。在GLFW中,我可以设置一个key_callback超出类定义的函数(一个自由函数):

WindowManager::WindowManager() {
    ...
    glfwSetKeyCallback(window_, key_callback);
    ...
}

// not a class function
void key_callback(GLFWwindow* window, int key, int scan code, int action, int mods) {
    ...
}

如何关联回调和WindowManager实例,以便设置keyboard_对象值?我不能使它key_callback成为成员函数,WindowManager因为那将是WindowManager类的成员,并且在C ++类的成员函数中,它们的名称会晃晃,因此该函数将不起作用。

Answers:


11

我有类似的问题。令人讨厌的是,关于使用glfwSetWindowUserPointer和glfGetWindowUserPointer的文档很少。这是我为您解决的问题的解决方案:

WindowManager::WindowManager() {
    // ...
    glfwSetUserPointer(window_, this);
    glfwSetKeyCallback(window_, key_callback_);
    // ...
}

void WindowManager::key_callback(GLFWwindow *window, int, int ,int, int) {
    WindowManager *windowManager =
      static_cast<WindowManager*>(glfwGetUserPointer(window));
    Keyboard *keyboard = windowManager->keyboard_;

    switch(key) {
        case GLFW_KEY_ESCAPE:
             keyboard->reconfigure();
             break;
     }
}

无论如何,因为这是将GLFW与C ++类一起使用的最佳结果之一,所以我还将提供将glfwWindow封装在C ++类中的方法。我认为这是最优雅的方法,因为它避免了必须使用globals,singletons或unique_ptrs,让程序员以更加OO / C ++-y的方式操纵窗口,并允许子类化(以头文件稍微混乱一些)。

// Window.hpp
#include <GLFW/glfw3.h>
class Window {
public:
    Window();
    auto ViewportDidResize(int w, int h)             -> void;
    // Make virtual you want to subclass so that windows have 
    // different contents. Another strategy is to split the
    // rendering calls into a renderer class.
    (virtual) auto RenderScene(void)                 -> void;
    (virtual) auto UpdateScene(double ms)            -> void;
    // etc for input, quitting
private:
    GLFWwindow *m_glfwWindow;

    // Here are our callbacks. I like making them inline so they don't take up
    // any of the cpp file
    inline static auto WindowResizeCallback(
        GLFWwindow *win,
        int w,
        int h) -> void {
            Window *window = static_cast<Window*>(glfwGetUserPointer(win));
            window->ViewportDidResize(w, h);
    }
    inline static auto WindowRefreshCallback(
        void) -> void {
            Window *window = static_cast<Window*>(glfwGetUserPointer(win));
            window->RenderScene(void);
    }
    // same for input, quitting
}

对于:

// Window.cpp
#include <GLFW/glfw3.h>
#include "Window.hpp"
Window::Window() {
    // initialise glfw and m_glfwWindow,
    // create openGL context, initialise any other c++ resources
    glfwInit();
    m_glfwWindow = glfwCreateWindow(800, 600, "GL", NULL, NULL);        

    // needed for glfwGetUserPointer to work
    glfwSetWindowUserPointer(m_glfwWindow, this);

    // set our static functions as callbacks
    glfwSetFramebufferSizeCallback(m_glfwWindow, WindowResizeCallback);
    glfwSetWindowRefreshCallback(m_glfwWindow, WindowRefreshCallback);
}

// Standard window methods are called for each window
auto
Window::ViewportDidResize(int w, int h) -> void
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
}

可以很容易地将它与WindowManager / InputManager类集成在一起,但是我认为让每个窗口自己管理会更容易。


几年后我回来了,看到了最新的答案。真的很好,谢谢你
ArmenB '16

在静态函数中,您正在创建一个类的新实例(即Window *window )。这如何解决问题?
CroCo '16

我注意到答案已更改为支持某些新的C ++功能。将函数的返回类型设置为auto,然后使用-> void进行提示有什么好处?
ArmenB

5

如您所知,回调必须是自由函数或静态函数。回调将a GLFWwindow*作为其第一个参数来代替自动this指针。

借助GLFW,您可以使用glwSetWindowUserPointerglfwGetWindowUserPointer存储和检索对WindowManager每个窗口Window实例的引用。

请记住,因为GLFW是纯C API,所以它不使用虚拟函数进行任何形式的直接多态处理。这样的API 始终假定自由函数(C根本没有类或成员函数,无论是虚拟的还是其他函数),并传递明确的“对象实例”作为参数(通常作为第一个参数; C没有this)。好的C API还包括用户指针功能(有时还称为“用户数据”),因此您不必使用全局变量。

旧答案:

如果您需要访问您WindowManager(或其他系统)中的其他数据,则要从回调访问它们,可能需要使它们可全局访问。例如,拥有一个std::unique_ptr<Engine>可以用来访问窗口管理器的全局变量,或者只是创建一个全局变量(如果需要,可以用“更好的单例” std::unique_ptr<WindowManager>代替std::unique_ptr)。

如果您需要多个窗口支持,则还将WindowManager包含一些数据结构以映射他们收到的GLFWwindow*' values to your ownWindow classes in some way, e.g. using astd :: unordered_map or the like. Your callback could then access the global and query the datastructure using theGLFWwindow *`以查找所需的数据。


谢谢您的帮助。在这样的情况下,这是通常的处理方式吗(使用全局unique_ptr来跟踪键盘输入)?我想避免这样的全局变量,而是希望将键盘的const指针传递给需要它的人,但这听起来像是不可能的,对吗?
ArmenB

1
通常不是unique_ptr,但是使用单例并不罕见。GLFW还为窗口提供了一个设置用户数据功能,可以避免使用全局功能。大多数“好的” C API都有类似的东西。可能会更新答案以建议我回到真实计算机时使用。
肖恩·米德迪奇
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.