bl4ckb0ne
bl4ckb0ne

Reputation: 1227

Use lambda as GLFWkeyfun

For one of my project, I am using GLFW3. And I am trying to give a C++11 lambda to the function GLFWSetKeyCallback, who takes as parameters a GFLWwindow, and a GLFWfunkey function, which is a typedef to void(* GLFWkeyfun) (GLFWwindow *, int, int, int, int).

The function is called from my Window class, here's the code

window.hpp

class Window
{
        public:
                Window(GLuint width, GLuint height, std::string title);
                ~Window();

                GLFWwindow* get() { return window_.get(); }
                bool is_open() { return open_; };
                void close();

        private:
                bool open_ = false;
                std::unique_ptr<GLFWwindow, DestroyWindow> window_;
                std::vector<Drawable*> drawables_;
};

And window.cpp

#include <pck/window.hpp>

Window::Window(GLuint width, GLuint height, std::string title) :
        open_(true)
{
        window_.reset(glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr));
    glfwMakeContextCurrent(window_.get());

        Global::width = width;
        Global::height = height;

        glfwSetKeyCallback(window_.get(), [this](GLFWwindow* window, int key, int scancode, int action, int mode){
                for(auto it : drawables_)
                        it->input(window, key, scancode, action, mode);
        });
}

Window::~Window()
{
        window_.reset();
}

void
Window::close()
{
        open_ = false;
        window_.reset();
}

And the error from the compiler (clang++ 3.8.1)

fatal error: no matching function for call to 'glfwSetKeyCallback'
        glfwSetKeyCallback(window_.get(), [this](GLFWwindow* window, i...
        ^~~~~~~~~~~~~~~~~~
/usr/include/GLFW/glfw3.h:3307:20: note: candidate function not viable: no
      known conversion from '(lambda at
      /path/to/file.cpp)' to
      'GLFWkeyfun' (aka 'void (*)(GLFWwindow *, int, int, int, int)') for 2nd argument
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun);

Can somebody point me to what I did wrong please?

Thanks!

Upvotes: 6

Views: 4787

Answers (3)

Guillaume Racicot
Guillaume Racicot

Reputation: 41800

A lambda that has capture cannot be converted into a function pointer. A function pointer is stateless, it points to a function. A lambda with capture has a state. It behave like an object. A member function cannot be called without an instance. That's way a lambda with capture cannot be converted into function pointer.

You'll have to change your function to that:

glfwSetKeyCallback(window_.get(), [](GLFWwindow* window, int key, int scancode, int action, int mode){ /* ... */ });

However, you can bypass this limitation of not being able to access this. GLFW given us a way to pass a state around. The struct GLFWWindow contains a void* member that you are free to change and access.

What you can do is something like this:

// Here we are setting the user pointer to `this`
glfwSetWindowUserPointer(window_.get(), this);

glfwSetKeyCallback(window_.get(), [](GLFWwindow* window, int key, int scancode, int action, int mode){
    // Here we retrieve the pointer we setted before. It will be equal to `this`
    auto& self = *static_cast<Window*>(glfwGetWindowUserPointer(window));
    for(auto&& it : self.drawables_) {
        it->input(window, key, scancode, action, mode);
    }
});

Upvotes: 7

marcinj
marcinj

Reputation: 50026

As krzaq said you cannot use closures if your want a function pointer. So you must not capture this as in your example.

If you need this anyway, then you might associate it with your GLFWwindow. I see you can use this API: glfwSetWindowUserPointer to associate GLFWwindow instance with instance of your Window class. Then inside your callback you can retrieve it with: glfwGetWindowUserPointer.

So your code would look like (pseudocode):

glfwSetWindowUserPointer(window_.get(), this);
glfwSetKeyCallback(window_.get(), [](GLFWwindow* window, int key, int scancode, int action, int mode){
                Window* pw = (Window*)glfwGetWindowUserPointer(window);
                for(auto it : pw->drawables_)
                        it->input(window, key, scancode, action, mode);
        });

Upvotes: 8

krzaq
krzaq

Reputation: 16431

Only lambdas with empty capture-list are convertible to plain old function pointers.

So, the problem is

[this]

instead of

[]

But if you need a stateful callback, then you're out of luck. As far as I can tell, this is not possible, not without evil gymnastics anyway.

That being said, most C APIs provide a context pointer allowing the callback to actually receive the state it's supposed to work on. It seems that the window parameter is that in your example.

Upvotes: 4

Related Questions