使用 lambda 作为 GLFWkeyfun

bl4*_*0ne 3 c++ lambda c++11

对于我的一个项目,我使用的是 GLFW3。我正在尝试为函数GLFWSetKeyCallback提供 C++11 lambda,该函数将 GFLWwindow 和 GLFWfunkey 函数作为参数,该函数是 void(* GLFWkeyfun) (GLFWwindow *, int, int, int, int) 的 typedef )。

该函数是从我的 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_;
};
Run Code Online (Sandbox Code Playgroud)

和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();
}
Run Code Online (Sandbox Code Playgroud)

以及来自编译器的错误(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);
Run Code Online (Sandbox Code Playgroud)

有人可以指出我做错了什么吗?

谢谢!

mar*_*inj 6

正如 krzaq 所说,如果您想要函数指针,则不能使用闭包。所以你不能this像你的例子那样捕获。

如果您this无论如何都需要,那么您可以将它与您的GLFWwindow. 我看到您可以使用此 API:glfwSetWindowUserPointer将 GLFWwindow 实例与您的Window类的实例相关联。然后在您的回调中,您可以使用以下命令检索它:glfwGetWindowUserPointer

所以你的代码看起来像(伪代码):

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);
        });
Run Code Online (Sandbox Code Playgroud)


Gui*_*cot 5

具有捕获的 lambda 不能转换为函数指针。函数指针是无状态的,它指向一个函数。带有捕获的 lambda 有一个状态。它的行为就像一个对象。没有实例就不能调用成员函数。这就是无法将带有捕获的 lambda 转换为函数指针的方式。

您必须将您的功能更改为:

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

但是,您可以绕过无法访问它的限制。GLFW 为我们提供了一种传递状态的方法。该结构GLFWWindow包含一个void*您可以自由更改和访问的成员。

你可以做的是这样的:

// 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);
    }
});
Run Code Online (Sandbox Code Playgroud)


krz*_*zaq 4

只有具有空捕获列表的 lambda 才可以转换为普通的旧函数指针。

所以,问题是

[this]
Run Code Online (Sandbox Code Playgroud)

代替

[]
Run Code Online (Sandbox Code Playgroud)

但如果您需要有状态的回调,那么您就不走运了。据我所知,这是不可能的,无论如何,如果没有邪恶的体操,这是不可能的。

话虽这么说,大多数 C API 都提供了一个上下文指针,允许回调实际接收它应该处理的状态。看来该window参数就是您示例中的参数。

  • @Bl4ckb0ne 如果您确实需要捕获列表,那么您根本不能使用 lambda 作为 C 函数的回调。 (3认同)