13
\$\begingroup\$

I'm trying to wrap my GLFW3 calls into a single class:

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

And I'm trying to setup a singleton keyboard class that collects the keys presses during execution. In GLFW I can set a key_callback function that is outside the class definition (a free function):

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

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

How can I associate my callback and my WindowManager instance so that I can set the keyboard_ object values? I cannot make the key_callback a member function of WindowManager because that would not work since that function would be a member of the the WindowManager class and in C++ member function of a class get their names dangled.

\$\endgroup\$

2 Answers 2

13
\$\begingroup\$

I had a similar problem to this. It's annoying that there's so little documentation on using glfwSetWindowUserPointer and glfGetWindowUserPointer. Here's my solution to your problem:

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

Anyway, as this is one of the top results for using GLFW with C++ classes, I will also provide my method of encapsulating a glfwWindow in a C++ class. I think that this is the most elegant way to do it, as it avoids having to use globals, singletons or unique_ptrs, lets the programmer manipulate the window in a much more OO/C++-y style, and allows subclassing (at the cost of a slightly more cluttered header file).

// 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
}

And for:

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

This can probably be quite easily integrated with a WindowManager/InputManager class, but I think it's easier to just have each window manage itself.

\$\endgroup\$
3
  • \$\begingroup\$ I came back after several years and saw the updated answer. Really good one thank you \$\endgroup\$ Commented Nov 29, 2016 at 1:10
  • \$\begingroup\$ In static functions, you're creating a new instance of a class (i.e. Window *window ). How this solve the problem? \$\endgroup\$
    – CroCo
    Commented Dec 4, 2016 at 7:46
  • \$\begingroup\$ I noticed the answer has changed to support some new C++ features. Is there any benefit of setting the function return type to auto, and then type hinting using -> void? \$\endgroup\$ Commented Dec 21, 2016 at 22:53
6
\$\begingroup\$

The callbacks must be free functions or static functions, as you've found out. The callbacks take a GLFWwindow* as their first argument in place of an automatic this pointer.

With GLFW you can use glfwSetWindowUserPointer and glfwGetWindowUserPointer to store and retrieve a reference to WindowManager or a per-window Window instance.

Remember that GLFW doesn't use virtual functions any kind of direct polymorphism as it's a pure C API. Such APIs always assume free functions (C has no classes or member functions at all, virtual or otherwise) and pass explicit "object instances" as parameters (usually as the first parameter; C has no this). Good C APIs also include the user pointer functionality (sometimes called "user data" among other things) so you don't have to use globals.


Old answer:

If you need to access other data in your WindowManager (or other systems), you might need to have them be globally accessible if you want to access it from callbacks. For instance, have a global std::unique_ptr<Engine> that you can use to access your window manager, or just make a global std::unique_ptr<WindowManager> (replace std::unique_ptr with something "better for singletons" if you wish).

If you want multiple window support, you'll also then have WindowManager contain some data structure to map GLFWwindow* values to your own Window classes in some way, e.g. using a std::unordered_map or the like. Your callback could then access the global and query the data structure using the GLFWwindow* they received to look up the data they need.

\$\endgroup\$
2
  • \$\begingroup\$ Thank you for your help. In a scenario like this, is this how it's normally handled (using a global unique_ptr for keeping track of keyboard inputs)? I wanted to avoid any global variables like this and preferred to pass around const pointers of keyboard to whomever it needs it but it sounds like this is not possible, am I right? \$\endgroup\$ Commented Jul 3, 2013 at 20:12
  • 1
    \$\begingroup\$ Not usually a unique_ptr, but it's not uncommon to use a singleton. GLFW also has a set user data function for windows that can avoid the need for a global. Most "good" C APIs have something similar. Might update answer to suggest that when I get back to a real computer. \$\endgroup\$ Commented Jul 3, 2013 at 21:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .