指向一个类成员的函数--glfw setKeycallback

vir*_*raj 24 c++ pointers class function glfw

我正在编写一个glfw应用程序,我将函数调用函数包装到一个简单的类中.我在设置密钥回调时遇到问题.我的课程定义为:

class GAME 
{
private:
    bool running;
public:
    GAME();
    int execute();
    void events(int, int);
    int loop();
    int render();
};
Run Code Online (Sandbox Code Playgroud)

执行功能是:

int GAME::execute() 
    {
        glfwOpenWindow(640, 320, 8, 8, 8, 8, 0, 0, GLFW_WINDOW);
        glfwSetWindowTitle("Viraj");
        glfwSetKeyCallback(events);
        running = true;
        while(glfwGetWindowParam(GLFW_OPENED))
        {
            glfwPollEvents();
            loop();
            render();
        }
        return 0;
    }
Run Code Online (Sandbox Code Playgroud)

在Visual Studio 2010上编译以下代码,给出错误:

error C3867: 'GAME::events': function call missing argument list; use '&GAME::events' to create a pointer to member

使用&GAME :: events提供:

error C2664: 'glfwSetKeyCallback' : cannot convert parameter 1 from 'void (__thiscall GAME::* )(int,int)' to 'GLFWkeyfun' 1> There is no context in which this conversion is possible

N0v*_*ber 39

其他答案中提供的代码示例没有描述如何将回调重定向到每个对象的成员函数,可能包含任意数量的对象.使您的类成为单例将限制您的设计,并且不会扩展到多个glfw窗口.

可扩展的解决方案是将glfw窗口用户指针设置为您的对象,然后在回调中获取它,并调用成员函数:

class MyGlWindow
{
public:
     void mouseButtonPressed();
};

void makeWindow()
{
    GLFWwindow* glfwWindow;
    MyGlWindow* myWindow;

    /* ... Initialize everything here ... */

    glfwSetWindowUserPointer(glfwWindow, myWindow);

    auto func = [](GLFWwindow* w, int, int, int)
    {
        static_cast<MyGlWindow*>(glfwGetWindowUserPointer(w))->mouseButtonPressed( /* ... */ );
    }

    glfwSetMouseButtonCallback(glfwWindow, func);
}
Run Code Online (Sandbox Code Playgroud)

此解决方案更短,适用于任意数量的窗口.


Mob*_*ent 18

我也遇到了另一个glfw回调函数的问题,但我不想将我的类方法声明为static,因为我需要访问其中的成员变量.于是,我就std::functionstd::bind给我结合实例方法的回调函数的能力,但以C回调工作时,不幸的是它不是一个选项.

GLFW FAQ "我如何使用C++方法作为回调"中也说明了这个问题的答案:

你不能使用常规方法作为回调,因为GLFW是一个C库,不知道对象和这个指针.如果您希望接收C++对象的回调,请使用静态方法或常规函数作为回调,将指针存储到您希望在回调中可到达的某个位置调用的对象,并使用它来调用对象上的方法.

但是,这鼓励我为我的回调类应用Singleton模式并将其集成如下:

  • 我的类的回调方法仍然是静态的,因此可以指定/用作glfw回调
  • 这个静态回调方法使用单例并将回调参数传递给实例方法
  • 此实例方法实际上处理回调参数,其优点是能够访问成员变量

这就是它的样子:

// Input.h (the actual callback class for glfwSetMouseButtonCallback)
class Input 
{
public:
    static Input& getInstance() // Singleton is accessed via getInstance()
    {
        static Input instance; // lazy singleton, instantiated on first use
        return instance;
    }

    static void mouseButtonCallback(int key, int action) // this method is specified as glfw callback
    {
        //here we access the instance via the singleton pattern and forward the callback to the instance method
        getInstance().mouseButtonCallbackImpl(key, action); 
    }

    void mouseButtonCallbackImpl(int key, int action) //this is the actual implementation of the callback method
    {
        //the callback is handled in this instance method           
        //... [CODE here]
    }

private:
    Input(void) // private constructor necessary to allow only 1 instance
    {
    }

    Input(Input const&); // prevent copies
    void operator=(Input const&); // prevent assignments
};
Run Code Online (Sandbox Code Playgroud)

在我的main.cpp中:

Input &hexmap = Input::getInstance(); // initialize the singleton

//The glfw callback is set up as follows:   
glfwSetMouseButtonCallback( &Input::mouseButtonCallback); // specifying the static callback method, which internally forwards it to the instance method
Run Code Online (Sandbox Code Playgroud)


Rus*_*hPL 15

有一种用于指向类成员方法的C++语法,但您无法将它们传递给C样式API.C理解函数调用和每个非静态对象方法,以你events的例子为例,看起来就像用C语言思考:void events(void* this, int, int);意味着除了标准参数之外的每个方法都会得到一个this静默传递的指针.

为了使你的eventsC兼容,做到这一点static void events(int, int);.这样它将遵循C调用语义 - 它不需要this指针传递.您还必须以某种其他方式将对象传递给此回调(如果在回调中需要此对象的数据).

  • GLFW现在有一个所谓的"用户指针",它是特定于窗口的,可以在任何地方访问,这使得可以将`this`设置为用户指针并在回调中调用它. (7认同)
  • 您能否提供将对象的数据传递给静态方法的示例? (3认同)

小智 5

我遇到了同样的问题,在阅读此线程后,我想出了一个类似的解决方案。我认为这种方式更干净一些。它基于静态函数,但它嵌套在我们设置所有内容的类中。

标题看起来像这样:

class Application
    {
    public:
        ...
    private:
        ...
        void MousePositionCallback(GLFWwindow* window, double positionX, double positionY);
        void KeyboardCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
        ...
        class GLFWCallbackWrapper
        {
        public:
            GLFWCallbackWrapper() = delete;
            GLFWCallbackWrapper(const GLFWCallbackWrapper&) = delete;
            GLFWCallbackWrapper(GLFWCallbackWrapper&&) = delete;
            ~GLFWCallbackWrapper() = delete;

            static void MousePositionCallback(GLFWwindow* window, double positionX, double positionY);
            static void KeyboardCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
            static void SetApplication(Application *application);
        private:
            static Application* s_application;
        };
    };
Run Code Online (Sandbox Code Playgroud)

和源代码:

void Application::GLFWCallbackWrapper::MousePositionCallback(GLFWwindow* window, double positionX, double positionY)
{
    s_application->MousePositionCallback(window, positionX, positionY);
}

void Application::GLFWCallbackWrapper::KeyboardCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    s_application->KeyboardCallback(window, key, scancode, action, mods);
}

void Application::GLFWCallbackWrapper::SetApplication(Application* application)
{
    GLFWCallbackWrapper::s_application = application;
}

Application* Application::GLFWCallbackWrapper::s_application = nullptr;

void Application::MousePositionCallback(GLFWwindow* window, double positionX, double positionY)
{
    ...
}

void Application::KeyboardCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    ...
}

void Application::SetCallbackFunctions()
{
    GLFWCallbackWrapper::SetApplication(this);
    glfwSetCursorPosCallback(m_window, GLFWCallbackWrapper::MousePositionCallback);
    glfwSetKeyCallback(m_window, GLFWCallbackWrapper::KeyboardCallback);
}
Run Code Online (Sandbox Code Playgroud)