C++ 指向类成员的函数-glfw setKeycallback

C++ 指向类成员的函数-glfw setKeycallback,c++,class,function,pointers,glfw,C++,Class,Function,Pointers,Glfw,我正在编写一个glfw应用程序,其中我将函数调用包装到一个简单的类中。我在设置回拨键时遇到问题。 我的班级被定义为: class GAME { private: bool running; public: GAME(); int execute(); void events(int, int); int loop(); int render(); }; 执行功能是: int GAME::execute() { glfw

我正在编写一个glfw应用程序,其中我将函数调用包装到一个简单的类中。我在设置回拨键时遇到问题。 我的班级被定义为:

class GAME 
{
private:
    bool running;
public:
    GAME();
    int execute();
    void events(int, int);
    int loop();
    int render();
};
执行功能是:

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;
    }
在Visual Studio 2010上编译以下代码时出现错误:

错误C3867:'GAME::events':函数调用缺少参数列表;使用“&GAME::events”创建指向成员的指针

使用&GAME::events可以提供:

错误C2664:'glfwSetKeyCallback':无法将参数1从'void(uu thiscall:GAME:*)(int,int)'转换为'GLFWkeyfun'

1、没有可能转换的上下文< /COD> >/P>> P>有一个指向类成员方法的C++语法,但不能将它们传递给C风格的API。C理解函数调用和每个非静态对象方法,以您的
events
为例,在C语言中类似于这样的想法:
void events(void*this,int,int)
意味着除了标准参数之外的每个方法也会获得一个
这个
指针,并以静默方式传递


要使您的
事件
C兼容,请使其
静态无效事件(int,int)。这样,它将遵循C调用语义-不需要传递
This
指针。您还必须以某种其他方式将对象传递给此回调(如果您在回调中需要此对象的数据)。

我在另一个glfw回调函数中也遇到了此问题,但我不想将我的类方法声明为
static
,因为我需要访问其中的成员变量。因此,我尝试了
std::function
std::bind
以使我能够将实例方法绑定为回调函数,但不幸的是,在使用C回调时,这不是一个选项

GLFW常见问题解答中也说明了此问题的答案:

不能将常规方法用作回调,因为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
};
在我的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

其他答案中提供的代码示例没有描述如何将回调重定向到每个对象成员函数,该函数可能包含任意数量的对象。将类设置为单例将限制您的设计,并且不会扩展到多个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);
}
类MyGlWindow
{
公众:
void mouseButtonPressed();
};
void makeWindow()
{
GLFWwindow*GLFWwindow;
MyGlWindow*myWindow;
/*…初始化这里的所有内容*/
glfwSetWindowUserPointer(glfwWindow,myWindow);
自动函数=[](GLFWwindow*w,int,int,int)
{
静态转换(glfwGetWindowUserPointer(w))->鼠标按钮按下(/*…*/);
}
glfwSetMouseButtonCallback(glfwWindow,func);
}

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

在头文件中将事件(int,int)转换为静态方法。这为我解决了问题

class GAME 
{
private:
    bool running;
public:
    GAME();
    int execute();
    static void events(int, int); //Changed here to static void 
    int loop();
    int render();
};

受N0vember答案的启发,我为您提供了更通用、更具活力的解决方案:

class MyGlWindow {
public:
    std::function<void(MyGlWindow*)> onClose;
    std::function<void(MyGlWindow*, int, int, int)> onMouseClick = [](auto self, int, int, int) { /*some default behavior*/ };
};

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

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

    glfwSetWindowUserPointer(glfwWindow, myWindow);

    #define genericCallback(functionName)\
        [](GLFWwindow* window, auto... args) {\
            auto pointer = static_cast<MyGlWindow*>(glfwGetWindowUserPointer(window));\
            if (pointer->functionName) pointer->functionName(pointer, args...);\
        }

    glfwSetWindowCloseCallback(glfwWindow, genericCallback(onClose));
    glfwSetMouseButtonCallback(glfwWindow, genericCallback(onMouseClick));

    myWindow->onMouseClick = [](auto self, int, int, int) {
        std::cout << "I'm such a rebel" << std::endl;
        self->onClose = [](auto self) {
            std::cout << "I'm such a rebellion" << std::endl;
        };
    };
}
类MyGlWindow{
公众:
std::函数onClose;
函数onMouseClick=[](autoself,int,int,int){/*一些默认行为*/};
};
void makeWindow(){
GLFWwindow*GLFWwindow;
MyGlWindow*myWindow;
/*…初始化这里的所有内容*/
glfwSetWindowUserPointer(glfwWindow,myWindow);
#定义genericCallback(函数名)\
[](GLFWwindow*窗口,自动…参数){\
自动指针=静态_转换(glfwGetWindowUserPointer(窗口))\
如果(指针->函数名)指针->函数名(指针,参数…)\
}
glfwSetWindowCloseCallback(glfwWindow,genericCallback(onClose));
glfwSetMouseButtonCallback(glfwWindow,genericCallback(onMouseClick));
myWindow->onMouseClick=[](自动自,int,int,int){

std::cout我也遇到了同样的问题,在阅读了这篇文章之后,我提出了一个类似的解决方案。我认为这样做会更简洁一些。它基于静态函数,但它嵌套在我们设置所有内容的类中

标题如下所示:

和源代码:

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

这是一个有用的讨论,可能的解决方案帮助我解决了同样的问题,我正在添加我的解决方案,以防它被证明是有用的

问题陈述

我的场景比BIC、L.Senionis和N0vember所述的场景更为一般。特别是,我的用例需要:

  • 通常,实例的数据必须可以被回调访问
  • 许多应用程序可以使用一组通用的响应处理程序来创建
  • 在应用程序中,可以创建任意数量的窗口
  • 附加到每个窗口的回调集合应该是混合的,并从可能的响应者的特定库中进行匹配
建议的解决方案使用情况

简单的单例设计不再解决这个问题,相反,我提供了一个
GLFWResponder
超类来处理所有
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);
}
// Implement custom responder
class MyResponder : public GLFWResponder {
  public:
    virtual void cursor_position_callback(GLFWwindow* w, double x, double y) {...}
    ... override relevant callbacks ...
 };

// in main ************************************************

// Assuming initialized GLFWwindow* my_window and my_other_window

MyResponder resp;
MyResponder resp2;  // Can be another subclass of GLFWResponder

// Two responders can respond to same window
resp.respond_to(my_window, GLFWResponder::CURSOR_POSITION);
resp2.respond_to(my_window, GLFWResponder::CURSOR_POSITION);

// One responder can respond to multiple windows
resp2.respond_to(my_other_window, GLFWResponder::CURSOR_POSITION);

// One window can have different handlers for different events
resp.respond_to(my_other_window, GLFWResponder::CURSOR_ENTER);
// GLFWResponder.h ************************************************
/**
 * Responder superclass that allows subclasses to handle events from multiple
 * GLFW windows (which have only C API for callbacks).
 * Callbacks are automatically cleaned up when responder goes out of scope.
 */
class GLFWResponder {
 public:
  virtual ~GLFWResponder();

  // Interface -----------------------------------
  enum GLFWEventType {
    CURSOR_POSITION = 0,
    CURSOR_ENTER = 1
    // TODO: add support for other callbacks
  };

  void respond_to(GLFWwindow* window, GLFWEventType event);

  bool does_respond_to(GLFWwindow* window, GLFWEventType event) const;

  // Subclasses implement ------------------------
  virtual void cursor_position_callback(GLFWwindow* window, double xpos, double ypos);

  virtual void cursor_enter_callback(GLFWwindow* window, int entered);

  // TODO: add support for other callbacks


  // Under the hood ------------------------------
  static std::set<GLFWResponder*> getResponders(GLFWwindow* windo, GLFWEventType event);

 private:
  // Windows and events that this instance responds to
  std::set<std::pair<GLFWwindow*, GLFWEventType> > enabled_events_;

  // Global responders keyed by events they respond to
  // (each responder knows which windows it responds to)
  static std::map<GLFWEventType, std::set<GLFWResponder*> > responders_;
}; 

// GLFWResponder.cpp **************************************************
namespace {

void cursor_position_callback_private(GLFWwindow* window, double xpos, double ypos) {
  for (GLFWResponder* r : GLFWResponder::getResponders(window, GLFWResponder::CURSOR_POSITION)) {
    r->cursor_position_callback(window, xpos, ypos);
  }
}

void cursor_enter_callback_private(GLFWwindow* window, int entered) {
  for (GLFWResponder* r : GLFWResponder::getResponders(window, GLFWResponder::CURSOR_ENTER)) {
    r->cursor_enter_callback(window, entered);
  }
}

} // namespace

std::map<GLFWResponder::GLFWEventType, std::set<GLFWResponder*> > GLFWResponder::responders_;

GLFWResponder::~GLFWResponder() {
  for (auto& pr : responders_) {
    pr.second.erase(this);
  }
  // TODO: also clean up window's callbacks
}

void GLFWResponder::respond_to(GLFWwindow* window, GLFWResponder::GLFWEventType event) {
  enabled_events_.insert(std::make_pair(window, event));
  responders_[event].insert(this);
  if (event == CURSOR_POSITION) {
    glfwSetCursorPosCallback(window, cursor_position_callback_private);
  } else if (event == CURSOR_ENTER) {
    glfwSetCursorEnterCallback(window, cursor_enter_callback_private);
  } else {
    // TODO: add support for other callbacks
    LOG(FATAL) << "Unknown GLFWResponder event: " << event;
  }
}

bool GLFWResponder::does_respond_to(GLFWwindow* window, GLFWEventType event) const {
  return enabled_events_.find(std::make_pair(window, event)) != enabled_events_.end();
}

std::set<GLFWResponder*> GLFWResponder::getResponders(
    GLFWwindow* window, GLFWEventType event) {
  std::set<GLFWResponder*> result;
  auto it = responders_.find(event);
  if (it != responders_.end()) {
    for (GLFWResponder* resp : it->second) {
      if (resp->does_respond_to(window, event)) {
        result.insert(resp);
      }
    }
  }
  return result;
}

void GLFWResponder::cursor_position_callback(
    GLFWwindow* window, double xpos, double ypos) {
  // TODO: fail with message "GLFWResponder::do_respond called on a subclass that does not implement a handler for that event"
}

void GLFWResponder::cursor_enter_callback(GLFWwindow* window, int entered) {
  // TODO: fail with message "GLFWResponder::do_respond called on a subclass that does not implement a handler for that event"
}