SuperSim135
SuperSim135

Reputation: 155

Can GLFW lambdas accept capture arguments?

I'm creating a GLFWKeyCallback and because of how simple it is I've decided to use a lambda. This callback modifies a member variable, so I have to pass this into the capture list. Here is what my code looks like so far:

glfwSetKeyCallback(window, 
        [this](GLFWwindow* window, int key, int scancode, int action, int mods)
        {
            if(action == GLFW_PRESS)
            {
                 //use a mutex
                 //Modify member variable
            }
        });

The problem is that whenever I pass this into the capture list, Visual Studio 2019 displays the following error:

no suitable conversion function from "lambda [] void (GLFWwindow *window, int key, int scancode, int action, int mods)->void" to GLFWKeyfun" exists

Have I missed something or is this code just invalid?

Upvotes: 0

Views: 906

Answers (2)

Matt Schwartz
Matt Schwartz

Reputation: 31

Adding onto the other answer.

I see glfwSetWindowUserPointer brought up a lot as a solution to this issue. It works fine (and I use it myself, since I don't know of any other solution), but it comes with a caveat that I haven't seen anyone mention:

You can only store one pointer per Window using this method. If some other code sets a different pointer to your window, all of a sudden your lambda won't work anymore. I can think of two workarounds here:

  1. When you retrieve the pointer in your lambda body, set it to a static variable. This way, it will persist across calls to the lambda, even if someone else sets a different pointer. Note: the static variable won't initialize until the first call to the lambda, so you'd be best to call the lambda once, yourself, after defining it.

  2. Define a object or map of pointers. Give GLFWSetUserPointer a pointer to that map. I can't think of any way to enforce this pattern, but if you have complete control over your app, you can store multiple pointers in associated with a Window this way.

Upvotes: 1

N. Shead
N. Shead

Reputation: 3938

The GLFW callbacks don't take lambdas, or function objects: they take plain old function pointers. A non-capturing lambda can be converted to a function pointer, but not a capturing one.

However, you can get a similar effect by using glfwSetUserPointer and glfwGetUserPointer. The lambda still can't be capturing, but you can recover the this pointer.

For example,

struct MyClass {
  GLFWwindow* window;

  MyClass(GLFWwindow* window) : window(window) {
    glfwSetWindowUserPointer(window, static_cast<void*>(this));

    glfwSetKeyCallback(window, 
      [](GLFWwindow* window, int key, int scancode, int action, int mods) {
        auto self = static_cast<MyClass*>(glfwGetWindowUserPointer(window));
        // can access member variables through `self`
      });
  }

  // make sure that either the class will last as long as GLFW will
  // or clean up the user pointer and callbacks in here
  ~MyClass() {
    // clean up
  }

  // don't be able to copy, probably, or bad things will happen
  MyClass(const MyClass&) = delete;
  MyClass& operator=(const MyClass&) = delete;
  // other things...
};

Upvotes: 7

Related Questions