2016-09-12 1 views
0

J'essaie d'enregistrer une fonction de membre de classe en tant que fonction de rappel (régulière). A moins d'avoir mal compris quelque chose, cela devrait être possible en utilisant std :: bind (en utilisant C++ 11). Je fais de la manière suivante:Enregistrement d'une fonction de membre de classe en tant que rappel d'une fonction à l'aide de std :: bind

std::function<void (GLFWwindow*, unsigned int)> cb = std::bind(&InputManager::charInputCallback, this, std::placeholders::_1, std::placeholders::_2); 

Ma fonction de rappel est définie de la manière suivante:

void InputManager::charInputCallback(GLFWwindow* window, unsigned int key) 

Je suis en mesure de tester cb immédiatement après la création à l'aide des données aléatoires:

cb(NULL, 0x62); 

Je peux confirmer que ces données sont envoyées correctement à la fonction de rappel en imprimant sur le terminal depuis l'intérieur.

Cependant, je souhaite enregistrer cette fonction dans GLFW afin que les pressions de touches de la fenêtre de programme soient envoyées à la fonction de rappel. Je fais comme ça:

glfwSetCharCallback(window, (GLFWcharfun) &cb); 

Comme je l'ai déjà dit: l'appeler manuellement fonctionne très bien. Lorsque je l'enregistre comme rappel, j'obtiens une erreur de segmentation chaque fois que j'appuie sur une touche et que GLFW essaie d'appeler la fonction de rappel.

Est-ce que std :: bind n'est pas ce que je cherche? Est-ce que je l'utilise incorrectement?

Modifier: Je ne pense pas que cette question est un doublon de How can I pass a class member function as a callback? comme il a été identifié comme. Alors que nous nous attaquons au même problème, je pose une question à propos de cette solution particulière, en utilisant std :: bind, qui est seulement mentionné mais jamais expliqué dans l'une des réponses à l'autre question.

+0

double possible de ([Comment puis-je passer une fonction de membre de classe comme un rappel?] Http: //stackoverflow.com/questions/400257/how-can-i-pass-a-class-member-function-as-a-callback) – Ari0nhh

+0

C++ n'a pas de garbage collection. 'this' ne reste pas magiquement juste parce que vous avez lié à un rappel. Si vous n'avez pas géré correctement la durée de vie, votre InputManager peut avoir été détruit au moment où GLFW appelle le rappel. Cela provoquerait un SegFault. Cependant, SegFaults peut aussi avoir d'autres causes - difficile à dire sans un exemple minimal. – MSalters

+0

@MSalters: Merci de le signaler, mais mon objet existe pour la durée de l'application entière. –

Répondre

0

Voici la déclaration de fonction:

GLFWcharfun glfwSetCharCallback ( GLFWwindow * window, 
            GLFWcharfun  cbfun 
           ) 

Où GLFWcharfun est défini comme typedef void(* GLFWcharfun) (GLFWwindow *, unsigned int)

Il y a un problème évident ici que vous ne recevez pas l'occasion de passer dans un objet « contexte » qui mappera automatiquement le rappel à une instance d'InputManager. Vous devrez donc exécuter le mappage manuellement à l'aide de la seule clé dont vous disposez: le pointeur de la fenêtre.

Voici une stratégie ...

#include <map> 
#include <mutex> 

struct GLFWwindow {}; 
typedef void(* GLFWcharfun) (GLFWwindow *, unsigned int); 

GLFWcharfun glfwSetCharCallback ( GLFWwindow * window, 
           GLFWcharfun cbfun 
           ); 


struct InputManager; 

struct WindowToInputManager 
{ 
    struct impl 
    { 
     void associate(GLFWwindow* window, InputManager* manager) 
     { 
      auto lock = std::unique_lock<std::mutex>(mutex_); 
      mapping_[window] = manager; 
     } 

     void disassociate(GLFWwindow* window, InputManager* manager) 
     { 
      auto lock = std::unique_lock<std::mutex>(mutex_); 
      mapping_.erase(window); 
     } 

     InputManager* find(GLFWwindow* window) const 
     { 
      auto lock = std::unique_lock<std::mutex>(mutex_); 
      auto i = mapping_.find(window); 
      if (i == mapping_.end()) 
       return nullptr; 
      else 
       return i->second; 
     } 

     mutable std::mutex mutex_; 
     std::map<GLFWwindow*, InputManager*> mapping_; 
    }; 

    static impl& get_impl() { 
     static impl i {}; 
     return i; 
    } 

    void associate(GLFWwindow* window, InputManager* manager) 
    { 
     get_impl().associate(window, manager); 
    } 

    void disassociate(GLFWwindow* window, InputManager* manager) 
    { 
     get_impl().disassociate(window, manager); 
    } 

    InputManager* find(GLFWwindow* window) 
    { 
     return get_impl().find(window); 
    } 

}; 

struct InputManager 
{ 
    void init() 
    { 
     // how to set up the callback? 

     // first, associate the window with this input manager 
     callback_mapper_.associate(window_, this); 

     // now use a proxy as the callback 
     glfwSetCharCallback(window_, &InputManager::handleCharCallback); 

    } 

    static void handleCharCallback(GLFWwindow *  window, 
          unsigned int ch) 
    { 
     // proxy locates the handler 
     if(auto self = callback_mapper_.find(window)) 
     { 
      self->charInputCallback(window, ch); 
     } 

    } 

    void charInputCallback(GLFWwindow *  window, 
          int ch) 
    { 
     // do something here 
    } 


    GLFWwindow* window_; 
    static WindowToInputManager callback_mapper_;  
}; 

Ou si vous préférez la fermeture:

#include <map> 
#include <mutex> 

struct GLFWwindow {}; 
typedef void(* GLFWcharfun) (GLFWwindow *, unsigned int); 

GLFWcharfun glfwSetCharCallback ( GLFWwindow * window, 
           GLFWcharfun cbfun 
           ); 


struct InputManager; 

struct WindowToInputManager 
{ 
    using sig_type = void (GLFWwindow *, unsigned int); 
    using func_type = std::function<sig_type>; 

    struct impl 
    { 
     void associate(GLFWwindow* window, func_type func) 
     { 
      auto lock = std::unique_lock<std::mutex>(mutex_); 
      mapping_[window] = std::move(func); 
     } 

     void disassociate(GLFWwindow* window) 
     { 
      auto lock = std::unique_lock<std::mutex>(mutex_); 
      mapping_.erase(window); 
     } 

     const func_type* find(GLFWwindow* window) const 
     { 
      auto lock = std::unique_lock<std::mutex>(mutex_); 
      auto i = mapping_.find(window); 
      if (i == mapping_.end()) 
       return nullptr; 
      else 
       return std::addressof(i->second); 
     } 

     mutable std::mutex mutex_; 
     std::map<GLFWwindow*, func_type> mapping_; 
    }; 

    static impl& get_impl() { 
     static impl i {}; 
     return i; 
    } 

    template<class F> 
    void associate(GLFWwindow* window, F&& f) 
    { 
     get_impl().associate(window, std::forward<F>(f)); 
     glfwSetCharCallback(window, &WindowToInputManager::handleCharCallback); 
    } 

    void disassociate(GLFWwindow* window) 
    { 
     // call whatever is the reverse of glfwSetCharCallback here 
     // 

     // then remove from the map 
     get_impl().disassociate(window); 
    } 

    const func_type* find(GLFWwindow* window) 
    { 
     return get_impl().find(window); 
    } 

    static void handleCharCallback(GLFWwindow* w, unsigned int ch) 
    { 
     auto f = get_impl().find(w); 
     // note - possible race here if handler calls disasociate. better to return a copy of the function? 
     if (f) { 
      (*f)(w, ch); 
     } 
    } 

}; 

struct InputManager 
{ 
    void init() 
    { 
     callback_mapper_.associate(window_, [this](auto* window, int ch) { this->charInputCallback(window, ch); }); 

    } 

    void charInputCallback(GLFWwindow * window, 
          int ch) 
    { 
     // do something here 
    } 


    GLFWwindow* window_; 
    WindowToInputManager callback_mapper_; 

}; 
+0

Je vois comment cela pourrait fonctionner. Bien sûr, je vais devoir accéder au paramètre window en utilisant 'this', en raison de la façon dont les paramètres sont poussés sur la pile, mais j'apprécie définitivement l'idée d'une carte. Je vais essayer avant de marquer cette réponse comme acceptée.Après tout, j'espérais une solution std :: bind. –

+0

@SvenG la seconde solution est une solution "bind" (via la carte incontournable). Mais j'ai utilisé un lambda au lieu de lier, car std :: bind est un anachronisme diabolique qui était nécessaire avant que nous ayons des lambdas. Cela devrait être évité aujourd'hui. –

+0

@SvenG Je viens de remarquer que toutes les fonctions glew doivent être appelées depuis le thread principal, et les callbacks arriveront aussi sur ce thread. Dans ce cas, il n'y a probablement pas besoin de mutex. –