在 C++ 中隐藏来自第 3 方库的回调原型

Hide callback prototype from a 3rd party library in C++

我正在使用具有此函数 glfwSetCursorPosCallback 的 glfw3 库,要使用该函数,您需要传递一个 GLFWwindow 指针和一个指向此原型函数的指针 void callback(GLFWwindow* window, double, double) .

我想创建一个 class WindowManager,它封装了该库的所有操作,所以我将 GLFWindow 保存为变量成员,并试图隐藏对 [=14 的调用=] 在 void WindowManager::setCursorCallback(void (*callback)(double, double))

void WindowManager::aCursorPosCallback(GLFWwindow* window, double xpos, double ypos)
{
    this->cursorPosCallback(xpos, ypos);
}

void WindowManager::setCursorCallback(void (*callback)(double, double)) {
    this->cursorPosCallback = callback;
    glfwSetCursorPosCallback(this->window, this->aCursorPosCallback);
}

所以 WindowManager 的客户端不必保留对 GLFwindow 的引用,像这样:

static void cursor_pos_callback(double xpos, double ypos)
{
   // HERE I have access to the values I care xpos and ypos
}

int main(int argc, char **argv) {
   WindowManager *wm = new WindowManager();
   wm->createWindow(WIDTH, HEIGHT)
   wm->setCursorCallback(cursor_pos_callback);
}

此代码无法编译,因为我试图像传递静态函数一样传递成员函数,但如果我可以修改第 3 方代码,我该如何实现此行为?

我什至尝试使用 labdas 但没有成功,编译器说 lambdas 只能在不捕获的情况下进行转换,所以我坚持使用它,任何线索都会有所帮助。

void WindowManager::setCursorCallback(void (*callback)(double, double)) {
    this->cursorPosCallback = callback;

    auto lamda =
    [callback](GLFWwindow* window, double xpos, double ypos)
    {
        printf("this works if no capture the callback");
        // callback(xpos, ypos);
    };

    glfwSetCursorPosCallback(this->window, (void(*)(GLFWwindow* window, double xpos, double ypos)) lamda);
}

更新:

基于@SparkyPotato 的评论,这应该有效并且确实有效。在使用 window、glfwSetWindowUserPointer(this->window, this);

将 WindowManager 设置为用户指针之前
static void aCursorPosCallback(GLFWwindow* window, double xpos, double ypos)
{
    WindowManager *wm = (WindowManager *) glfwGetWindowUserPointer(window);
    wm->cursorPosCallback(xpos, ypos);
}

void WindowManager::setCursorCallback(void (*callback)(double, double)) {
    this->cursorPosCallback = callback;
    glfwSetCursorPosCallback(this->window, aCursorPosCallback);
}

GLFW 允许您设置一个 window 用户指针,可以是任何 void*。然后您可以检索 void*,并将其转换为您想要的任何类型。我认为函数为 glfwSetWindowUserPointer()glfwGetWindowUserPointer().

你可以做的是让 WindowManager 注册所有回调,并将用户指针设置为 this。然后,当用户希望设置某些内容时,您可以在 WindowManager 本身中使用 std::function (这样他们就可以传递 lambda 或任何他们想要的东西),然后从您的 GLFW 回调中调用该函数reinterpret_cast将 window 用户指针指向 WindowManager

示例:

class WindowManager
{
public:
  template<typename T>
  void SetCursorPosCallback(const T& userCallback)
  {
    m_UserCallback = userCallback;
  }

  void CreateWindow(/* args */)
  {
    GLFWwindow* window = glfwCreateWindow(/* args */);
    glfwSetWindowUserPointer(window, this);
    glfwSetCursorPosCallback(window, &WindowManager::CursorPosCallback);
  }

private:
  static void CursorPosCallback(GLFWwindow* window, double x, double y)
  {
    WindowManager* manager = reinterpret_cast<WindowManager*>(glfwGetWindowUserPointer(window));
    manager.m_UserCallback(x, y);
  }

  std::function<void(double, double)> m_UserCallback;
};