Reputation: 1227
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
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
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
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