c++callbackglfw

Argument of type `<C++ class method>` is incompatible with parameter of type `GLFWwindowsizefun` when using GLFW inside a class


I'm currently refactoring my code into a class (VkAPP), and stumbled upon this error:

argument of type "void (VkAPP::)(GLFWwindow window, int width, int height)" is incompatible with parameter of type "GLFWwindowsizefun"

Here is the function in question:

void onWindowResized(GLFWwindow* window, int width, int height) {
    VkSurfaceCapabilitiesKHR surfaceCapabilities;
    vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevices[0], surface, &surfaceCapabilities);

    if (width > surfaceCapabilities.maxImageExtent.width) width = surfaceCapabilities.maxImageExtent.width;
    if (height > surfaceCapabilities.maxImageExtent.height) height = surfaceCapabilities.maxImageExtent.height;

    if (width == 0 || height == 0) return;

    WIDTH = width;
    HEIGHT = height;

    recreateSwapchain();
}

Here is the call:

glfwSetWindowSizeCallback(window, onWindowResized);

The funtion and the call are in the same class.

If I turn this into:

static void onWindowResized

I get:

a nonstatic member reference must be relative to a specific object

for physicalDevices[0], surface, WIDTH, HEIGHT, and recreateSwapchain()


Solution

  • Let's start with the first error message you get.

    The GLFWwindowsizefun is defined like this:

    typedef void(* GLFWwindowsizefun) (GLFWwindow *window, int width, int height)
    

    On the other hand, the (non static) onWindowResized method of your C++ class has an implicit class instance pointer.

    In other words, your VkAPP C++ class' non-static onWindowResized method declared like this:

    // Inside VkAPP class
    void onWindowResized(GLFWwindow* window, int width, int height)
    

    has an implicit parameter that is a pointer to the VkAPP class instance (i.e. the classic this pointer in C++):

    void VkAPP::onWindowResized(VkAPP*      this, /* implicit VkAPP* 'this' pointer */ ,
                                GLFWwindow* window, 
                                int         width, 
                                int         height)
    

    So, the C++ compiler signaled to you the mismatch between the expected GLFWwindowsizefun (which doesn't have the implicit this pointer), and your VkAPP::onWindowResized method (which does have the implicit this pointer).

    How can you resolve this mismatch?

    Well, you can define a static method in your VkAPP C++ class, like this:

    // Inside VkAPP class
    // 
    // Note: the method is *static*
    static void onWindowResizedStatic(GLFWwindow* window, int width, int height)
    

    Note that static methods do not have the implicit this pointer, so the above method corresponds to the GLFWwindowsizefun typedef.

    So you can pass it to the glfwSetWindowSizeCallback function call:

    // You had this line, which didn't work as onWindowResized was not static:
    //
    //     glfwSetWindowSizeCallback(window, onWindowResized);  <<-- error
    //
    // Pass a *static* method as callback, instead:
    //
    glfwSetWindowSizeCallback(window, onWindowResizedStatic);
    

    Unfortunately, as the other error message you got revealed, you cannot access instance-specific data members like physicalDevices[0], surface, WIDTH, HEIGHT, etc., from a static method of your class.

    So, the idea here is to use the static method as a place to just forward the call to the instance-specific method of your class, e.g.:

    // Inside VkAPP class
    static void onWindowResizedStatic(GLFWwindow* window, int width, int height) {
        // Somehow get the instance-specific "this" pointer
        VkAPP* pThis = ... /* we'll see that later */;
    
        // Forward the call to the non-static method callback:
        pThis->onWindowResized(window, width, height);
    }
    

    Now, how can you retrieve the this pointer from the static method above?

    Well, during initialization (e.g. in the constructor of your C++ class, or in another initialization method), once you have a valid GLFWwindow object (for example, created with glfwCreateWindow), you can invoke the glfwSetWindowUserPointer function to set the instance-specific this pointer as the generic "user pointer" associated to the window object:

    // Inside a method of your VkAPP C++ class, during initialization:
     
    // Set the "this" pointer as window's user pointer for later retrieval
    glfwSetWindowUserPointer(window, this);
    

    Then, the this pointer can be later retrieved invoking the symmetric glfwGetWindowUserPointer:

    // Inside VkAPP class
    static void onWindowResizedStatic(GLFWwindow* window, int width, int height) {
        // Get the instance-specific "this" pointer 
        // that was set before during initialization
        VkAPP* pThis = static_cast<VkAPP*>(glfwGetWindowUserPointer(window));
    
        // Forward the call to the non-static method callback:
        pThis->onWindowResized(window, width, height);
    }
    

    Note that, since your onWindowResized method is not static, you can use all the instance-specific data members like physicalDevices[0], surface, etc., or invoke other member functions like recreateSwapchain(), that your second error message was complaining about.