3 votes

Délier un gestionnaire d'événement lambda

Comment puis-je délier un gestionnaire d'événement que j'ai lié comme indiqué ci-dessous ?

MyFrame::MyFrame()
{
    Bind(wxEVT_COMMAND_MENU_SELECTED,
         [](wxCommandEvent&) {
            // Do something useful
         },

         wxID_EXIT);
}

Merci beaucoup pour la première réponse. J'ai ajouté quelques informations supplémentaires.

La possibilité de délier un gestionnaire d'événement en utilisant un foncteur concret est documentée et fonctionne bien, mais si vous utilisez le style lambda de C++ 11 pour lier quelque chose, plus tard il n'y a pas de foncteur disponible pour appeler la méthode de déliaison. Et cela pose des problèmes si la méthode wxEvtHandler devraient être détruits.

Existe-t-il une "astuce" ? Si ce n'est pas le cas, je ne vois pas de réel cas d'utilisation pour lier en utilisant des foncteurs lambda. J'espère que je me trompe

Merci beaucoup.
Hacki

3voto

yeputons Points 2816

Documentation du wxWidget spécifie la manière exacte Unbind fonctionne pour les foncteurs (et les lambdas également) :

Actuellement, les foncteurs sont comparés par leur adresse, ce qui, malheureusement, ne fonctionne pas correctement si la même adresse est réutilisée pour deux objets foncteurs différents.

Donc, si vous voulez Unbind un certain foncteur de manière fiable, vous devez enregistrer le foncteur dans un endroit spécial qui est partagé par Bind y Unbind et transmettez ensuite exactement le même objet aux deux fonctions (elles prennent const-références malgré une documentation qui ne le reflète pas).

Cela s'applique aussi bien aux lambdas qu'aux foncteurs, donc ce n'est même pas spécifique à C++11. Si vous voulez Unbind quelque chose, ce quelque chose doit avoir une adresse fixe en mémoire. Par conséquent, il doit avoir un nom. Cela tue un peu la beauté des lambdas.

Donc, ça devrait marcher :

static auto event_handler = [](wxCommandEvent&) {
  // Do something
};
// ...
Bind(..., event_handler, ...);
// ...
Unbind(..., event_handler, ...);

Mais même cela ne fonctionne pas (ou fonctionne, selon votre chance avec l'emplacement des variables temporaires) :

struct EventHandler {
  void operator()(wxCommandEvent&) const {
    // Do something
  }
};
// ...
Bind(..., EventHandler(), ...);
// ...
Unbind(..., EventHandler(), ...);

2voto

hleinone Points 480

Malheureusement, il n'y a pas de moyen de le faire actuellement, vous devrez stocker votre lambda dans un objet pour lui donner une identité, par ex.

auto const handler = [](wxCommandEvent&) { ... };

// To bind it:
Bind(wxEVT_MENU, handler, wxID_EXIT);

// To unbind it:
Unbind(wxEVT_MENU, handler, wxID_EXIT);

0voto

Igor Points 1671

Lorsque l'exécution du programme est terminée, tous les gestionnaires d'événements sont dissociés de tous les contrôles. Ceci est vrai même pour les lambdas.

Il est très rare que vous ayez besoin de délier quelque chose à l'intérieur du code utilisateur, mais si vous le faites, la réponse de @Caleth fonctionnera.

-2voto

Caleth Points 17517

Vous ne pouvez pas vous délier lorsque l'appel à Bind contient un lambda, comme dans votre exemple. Si vous stockez le lambda, vous pouvez le délier à partir de celui-ci, par ex.

class MyFrame 
{ 
    ... 
    std::function<void()> DoUnbind;
}

MyFrame::MyFrame()
{
    auto DoSomethingUseful = [](wxCommandEvent&) {
            // Do something useful
         };

    Bind(wxEVT_COMMAND_MENU_SELECTED,
         DoSomethingUseful,
         wxID_EXIT);

    DoUnbind = [DoSomethingUseful](){
        Unbind(wxEVT_COMMAND_MENU_SELECTED,
               DoSomethingUseful,
               wxID_EXIT);
    };
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X