【问题标题】:Can GLFW lambdas accept capture arguments?GLFW lambda 可以接受捕获参数吗?
【发布时间】:2020-02-02 00:33:24
【问题描述】:

我正在创建一个 GLFWKeyCallback,由于它非常简单,我决定使用 lambda。这个回调修改了一个成员变量,所以我必须将 this 传递到捕获列表中。到目前为止,我的代码如下所示:

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

问题是每当我将 this 传递到捕获列表中时,Visual Studio 2019 都会显示以下错误:

不存在从“lambda [] void (GLFWwindow *window, int key, int scancode, int action, int mods)->void”到 GLFWKeyfun”的合适转换函数

是我遗漏了什么还是这段代码无效?

【问题讨论】:

  • glfwSetKeyCallback 不需要 lambda - 它需要一个普通的旧函数指针。无捕获的 lambda 可转换为函数指针;没有捕获的 lambda(没有地方可以存储捕获的数据)。
  • 这能回答你的问题吗? Passing C++ method as function pointer。我的回答显示了在调用裸函数的情况下使用成员函数的混乱性质。
  • 您可以使用glfwSetWindowUserPointer 给窗口一个用户定义的指针(就像this 一样)并使用glfwGetWindowUserPointer 从回调中取回它
  • 当问题结束时,我刚刚完成了一个演示 glfwSetWindowUserPointer 的答案 :( 不过,是的,设置和检索 this 作为窗口指针是正确的方法。
  • 我的回答显示了解决您问题的方法。关键是你需要一个类的实例来改变成员变量。带有捕获的 lambda 永远不会起作用——它是一个动态创建的类,使用函数指针和捕获值作为数据成员。

标签: c++ lambda glfw


【解决方案1】:

GLFW 回调不采用 lambda 或函数对象:它们采用普通的旧函数指针。非捕获的 lambda 可以转换为函数指针,但不能转换为捕获的。

但是,您可以使用glfwSetUserPointerglfwGetUserPointer 获得类似的效果。 lambda 仍然无法捕获,但您可以恢复 this 指针。

例如,

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...
};

【讨论】:

  • 如何销毁用户指针?还有,为什么不能复制对象?
  • @SuperSim135 清理只需将用户指针设置回nullptr 或其他任何东西,回调也是如此。对象不应该是可复制的原因是用户指针实际上是一个全局变量——只能有一个用户指针。因此,拥有多个班级是有问题的。
  • (也就是说,回过头来看,我忽略了每个GLFWwindow* 实例可以有一个单独的用户指针。该类仍然不应该是可复制的,但可以有多个类的一个实例来处理例如多个窗口。)
  • 您的回答有问题,那就是它只允许访问可公开访问的成员。我认为我真正的解决方案是简单地使用另一个 API 或简化我的程序结构。 GLFW 可能不是最佳选择。
  • @SuperSim135 当然,因为 lambda 不是您班级的成员。如果您需要访问私有参数,您可以在您的类中添加一个函数来完成这项工作并让 lambda 立即调用该函数,使用您从用户指针中拉出的 self 实例。
【解决方案2】:

添加到另一个答案。

我看到 glfwSetWindowUserPointer 提出了很多作为解决此问题的方法。它工作正常(我自己使用它,因为我不知道任何其他解决方案),但它附带一个我没有看到任何人提到的警告:

使用此方法,每个窗口只能存储一个指针。如果其他一些代码为您的窗口设置了不同的指针,突然之间您的 lambda 将不再工作。我可以在这里想到两种解决方法:

  1. 当您在 lambda 主体中检索指针时,请将其设置为静态变量。这样,即使其他人设置了不同的指针,它也会在对 lambda 的调用中持续存在。注意:静态变量在第一次调用 lambda 之前不会初始化,因此您最好在定义 lambda 后自己调用一次。

  2. 定义一个对象或指针映射。给 GLFWSetUserPointer 一个指向该映射的指针。我想不出任何方法来强制执行这种模式,但是如果您可以完全控制您的应用程序,您可以通过这种方式将多个指针与 Window 关联起来。

【讨论】:

猜你喜欢
  • 2012-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-20
  • 2017-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多