Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Give static function access to data without passing the data as a parameter

I am using GLFW for a window in my C++ application and I am trying to get input events using GLFW's callbacks. For instance, this is how you get key events:

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods){
    // Do something with event data.
}

int main(){
    // initialize window (I have no problems with this step)

    glfwSetKeyCallback(window, key_callback);
    // Now when a key is pressed in the window it will call this function.
}

The Problem:

In my key_callback I want to use a variable that is declared outside of the key_callback function and since I cannot alter the parameters of key_callback I cannot pass a reference to the variable.

Now, in the given example above, I could simply declare my variable outside of int main() and both key_callback and int main() would have access to the same instance of the variable.

My Desired Use:

I want to have a WindowWrapper class that creates and manages the life cycle of the glfwWindow, this would include setting the event callbacks.

WindowWrapper.h

// Includes
class WindowWrapper{
private:
    Centrum* g_centrum_;
    GLFWwindow* window_;
    std::thread thread_;
public:
    WindowWrapper();
    WindowWrapper(Centrum* g_centrum);
    ~WindowWrapper();
private:
    // Callbacks
    static void key_callback(
        GLFWwindow* window, int key, int scancode, int action, int mods
        );
};

WindowWrapper.cpp

WindowWrapper::WindowWrapper(Centrum* g_centrum){
    g_centrum_ = g_centrum;
    // Initialize window

    glfwSetKeyCallback(window_, key_callback); // Problems

    // Window loop and OpenGL stuff
}
WindowWrapper::~WindowWrapper(){
    thread_.join(); // Don't worry about this, it works but, I will make it safer.
    glfwDestroyWindow(window_);
    printf("WindowWrapper Completely Destructed!\n"); // For testing purposes
}

void WindowWrapper::key_callback(
    GLFWwindow* window, int key, int scancode, int action, int mods
    ){
    // This function is declared static in the class declaration.
    // And as a result I cannot use g_centrum_ since it is a non-static variable
    // Essentially I want to be able to access g_centrum_ from this function
    g_centrum_->input_eventmanager_->key_eventmanager_->
        TriggerKeyEvent(key, action, mods);
}

The first way I thought to do this was to pass a reference to g_centrum, but GLFW will not all any deviation from the parameters for a callback.

My second attempt was to declare and define the callbacks in the constructor, but you cannot do that.

My third attempt was to make g_centrum_ static, but I would have to give it the reference outside of the constructor and I don't think that to be an elegant solution.

like image 437
MichaelMitchell Avatar asked Jan 09 '23 05:01

MichaelMitchell


2 Answers

Use glfwSetWindowUserPointer() to associate your wrapper pointer to the window before you register your callback. When your callback is called, you can use glfwGetWindowUserPointer() to retrieve it. These APIs are described in the GLFW documentation.

Window user pointer

Each window has a user pointer that can be set with glfwSetWindowUserPointer and fetched with glfwGetWindowUserPointer. This can be used for any purpose you need and will not modified by GLFW throughout the life-time of the window.

As an example, you could do this in your WindowWrapper constructor:

WindowWrapper::WindowWrapper(Centrum* g_centrum){
    g_centrum_ = g_centrum;
    // Initialize window first
    ...
    // Now, associate the wrapper to the window
    glfwSetWindowUserPointer(window_, this);

    glfwSetKeyCallback(window_, key_callback); // Problems

    // Window loop and OpenGL stuff
}

Then, in your callback:

void WindowWrapper::key_callback(
    GLFWwindow* window, int key, int scancode, int action, int mods
    ){

    void *data = glfwGetWindowUserPointer(window);  
    WindowWrapper *w = static_cast<WindowWrapper *>(data);

    w->g_centrum_->input_eventmanager_->key_eventmanager_->
        TriggerKeyEvent(key, action, mods);
}
like image 52
jxh Avatar answered Jan 24 '23 14:01

jxh


You could have a static map that maps each GLFWindow* to its corresponding WindowWrapper:

private:
    static std::map<GLFWindow*, WindowWrapper*> m_instanceMap;
    ...

Then in the constructor, you add an entry to this map:

m_instanceMap.insert(std::make_pair(window_, this));

In the destructor, remove it:

m_instanceMap.erase(window_);

Now, in the (static) callback method, you can look up the class instance, and invoke a per instance method:

void WindowWrapper::key_callback(
    GLFWwindow* window, int key, int scancode, int action, int mods)
{
    WindowWrapper* pThis = m_instanceMap[window];
    pThis->keyHandler(key, scancode, action, mods);
}

Where keyHandler() is a regular instance method.

like image 27
Reto Koradi Avatar answered Jan 24 '23 14:01

Reto Koradi