2

I'm using glfw for setting up the OpenGL visualization. I would like to set the callback function as non-static function to access the class variables. However, when I did below I got the error "reference to non-static member function must be called". I know that the callback function must be static in this case, but is there a way to achieve the objective?

class Viewer
{
public:
    Viewer()
    {
        glfwSetErrorCallback(errorCallback);
    }

private:

    void errorCallback(int error, const char* description)
    {
        // print more class variables
        std::cerr << "Error: " << description << std::endl;
    }

};
3
  • 1
    Nope it's a C interface glfw.org/docs/3.0/group__error.html without an additional parameter. So there is no way to pass a pointer to the class you want to handle the error. Commented Jul 5, 2022 at 13:13
  • Why so you need to pass a member function here anyway? Do you have more than one error handler instance?
    – catnip
    Commented Jul 5, 2022 at 14:04
  • @PaulSanders as said, errorCallback shall be able to access class variables. It can't if it is static.
    – kstn
    Commented Jul 6, 2022 at 7:16

1 Answer 1

0

GLFW is a C interface, and does not support C++ objects. For the window, monitor and joystick callback functions, you can store a user pointer for each of these objects, retrieve it in the callback functions, and use that to call a member function (method) of your objects.

This is not possible with the error callback. Instead, you can use a global variable to store a pointer to a local error handler. The global error callback calls this error handler if this variable is not a nullptr. You set this pointer before calling a GLFW function, and restore it or nullify it afterwards.

Note that this simple approach only works for single-threaded applications. If you are calling GLFW functions from multiple threads, you need to ensure the thread safety. GLFW 4 will likely improve upon this.

My C++ is very rusty, so don't expect this to work out of the box, but I believe it demonstrates what you can do:

typedef void (*glfw_error_handler_function)(void* user_data, int error_code, const char* description);

thread_local static struct glfw_error_handler_t {
    void* user_data;
    glfw_error_handler_function handler;
} glfw_error_handler = { NULL, NULL };

static void global_glfw_error_handler(int error_code, const char* description) {
    if (glfw_error_handler.handler != NULL) {
        glfw_error_handler.handler(glfw_error_handler.user_data, error_code, description);
    }
}

class Window {
private:
    GLFWwindow* window;
    
    void errorCallback(int error, const char* description) {
        std::cerr << "Error: " << description << std::endl;
    }
public:
    Window() {
        window = glfwCreateWindow();
        // error handling omitted for simplicity
    }
    
    void requestAttention() {
        glfw_error_handler_t previousState = glfw_error_handler;
        glfw_error_handler.user_data = this;
        glfw_error_handler.handler = Window::errorCallback;
        
        glfwRequestWindowAttention(window);
        
        // Restore previous error handler state
        glfw_error_handler = previousState;
    }
}

static int main() {
    glfwSetErrorCallback(global_glfw_error_handler);

    if (!glfwInit()) {
        return EXIT_FAILURE;
    }

    Window window();
    window.requestAttention();

    return 0;
}

Another possibility may be to throw a C++ error in the global error handler, and catch it at the location that calls the offending GLFW function.

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