Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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.

share|improve this question

2 Answers 2

up vote 5 down vote accepted

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 glwSetWindowUserPointer 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 ownWindowclasses in some way, e.g. using astd::unordered_mapor the like. Your callback could then access the global and query the datastructure using theGLFWwindow*` they received to look up the data they need.

share|improve this answer
    
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? –  Armen B. Jul 3 '13 at 20:12
1  
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. –  Sean Middleditch Jul 3 '13 at 21:07

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() {
    // set the window pointer to the keyboard object
    glfwSetUserPointer(window_, keyboard_);
    glfwSetKeyCallback(window_, key_callback_);
    // set it back to the window
    glfwSetUserPointer(window_, window_);
    // ...
}

void WindowManager::key_callback(GLFWwindow *window, int, int ,int, int) {
    keyboard_ *keyboard =
      static_cast<keyboard_*>(glfwGetUserPointer(window));
    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.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.