IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

pointeur vers fonctions


Sujet :

C++

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2003
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2003
    Messages : 17
    Par défaut pointeur vers fonctions
    Bonjour a tous!

    Je suis entrain de développer une librairie d'interface graphique opengl opensource (https://github.com/3dsman/snice_ui) qui avance bien mais je viens de tomber sur un problème que je n'avais pas anticipé dans mon architecture...

    En gros quand je rée un bouton par exemple je peut créer une fonction de callback pour exécuter différentes actions (ici je me contente d'afficher le bouton enfoncé):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    // fonction de callback du bouton
    void test(W_button* caller)
    {
        caller->SetPressed(true);
        std::cout<<"le bouton a été appuyé";
    };
     
    int main(int argc, char *argv[])
    {
        // creation de la fenetre avec un viewport par default
        pSnice_UI = new Snice_UI(800,600,50);
     
        // recuperaion du viewport
        pViewport = pSnice_UI->GetViewport();
     
        // creation d'un bouton
        pValid = new W_button(200,100, 120,20, "COOL");
     
        // attache du bouton au viewport
        pViewport->AddChild(pValid);
     
        //signalisation de la fonction de callback
        pValid->OnClick(test);
     
        [...]
    }
    Dans la classe W_button j'ai (en version tres tres epurée ):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    class W_button : public UI_widget
    {
    protected
      void (*onClick)(W_button* caller) = NULL;
     
    public:
    void W_button::OnClick(void (*function)(W_button* caller))
    {
        onClick = function;
    }
     
    UI_base* W_button::OnLButtonUp(int x, int y)
    {
            if (Hittest(x,y))
            {
                if(onClick) onClick(this);
            }
        return 0;
    }
    }
    Tout ca marche tres bien dans le ca ou je crée tous les boutons a la main mais j'aimerais bieen maintenant avoir des dialog boxes (un widget de texte et deux boutons par exemple).
    Le probleme c'est que mes fonctions de callback ne pointent pas sur des fonctions de classe.
    Je ne peut donc pas par exemple avoir une classe D_toto qui aurait une fonction test pour recuperer le callback du bouton...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    void D_toto::D_toto()
    {
        // creation d'un bouton
        pValid = new W_button(200,100, 120,20, "COOL");
     
        // attache du bouton a ma dialogbox
        AddChild(pValid);
     
        //signalisation de la fonction de callback
        pValid->OnClick(this->test);
    }
     
    void D_toto::test(W_button* caller)
    {
        caller->SetPressed(true);
        std::cout<<"le bouton a été appuyé";
    };
    Je vois bien que j'ai fait une erreur d'architecture mais je ne vois pas de solution pour contourner le problème...
    Comment ca se fait ce genre de trucs en général?

    Merci d'avance de vos réponses.

  2. #2
    Membre émérite
    Homme Profil pro
    Recherche du travail
    Inscrit en
    Août 2004
    Messages
    561
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Recherche du travail

    Informations forums :
    Inscription : Août 2004
    Messages : 561

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2003
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2003
    Messages : 17
    Par défaut
    Je ne vois pas trop ce que ca changerais dans mon cas.
    Pourrais tu m'indiquer comment l'utiliser ici stp?

    Dans tous les cas une fonction membre devra etre stockée dans un autre variable non?
    std::function<void(const D_toto&, int)> onClick = &D_toto::OnClick;

    Par contre je viens de voir dans un de tes liens l'utilisation de fonctions statiques avec un handler sur l'objet appelé:
    https://isocpp.org/wiki/faq/pointers...fnptr-vs-fnptr

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    class D_toto : public UI_dialog
    {
    protected
        void OnBtnClick(W_button* caller);
        W_button pValid;
        static D_toto* handler;
    public:
        static void OnBtnClickStat(W_button* caller);
    }
     
    void D_toto::D_toto()
    {
        // creation d'un bouton
        pValid = new W_button(200,100, 120,20, "COOL");
     
        // attache du bouton a ma dialogbox
        AddChild(pValid);
     
        //signalisation de la fonction de callback
        pValid->OnClick(OnBtnClickStat);
    }
     
    void OnBtnClickStat(W_button* caller)
    {
        // on verifie que le handler est bien remplis
        if (D_toto::handler)
            D_toto::handler->OnBtnClick(W_button* caller);
    }
    Sachant que je peut remplir le handler avec un pointeur sur l'objet courant (ma boite de dialogue) ca peut le faire non?

  4. #4
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 760
    Par défaut
    std::function est une classe générique qui prend n'importe quoi qui possède operator(): lambda, fonction, foncteur.
    std::mem_fn est une fonction qui retourne un foncteur pour appeler une fonction membre.

    Donc le type de caller devient std::function<void(W_button*)>.

    Et pValid->OnClick(this->test); devient pValid->OnClick([this](W_button*){ this->test(); });.

  5. #5
    Membre émérite
    Homme Profil pro
    Recherche du travail
    Inscrit en
    Août 2004
    Messages
    561
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Recherche du travail

    Informations forums :
    Inscription : Août 2004
    Messages : 561
    Par défaut
    Utiliser la std est préférable car elle apporte des sécurités notamment l'exception bad_function_call. Disons que c'est plus orienté c++, c'est comme pour le cast des types. On privilégiera l'utilisation de static_cast au lieu du cast c.

    Dans tous les cas une fonction membre devra etre stockée dans un autre variable non?
    std::function<void(const D_toto&, int)> onClick = &D_toto::OnClick;
    Tu peux utiliser le type std::function<void(const D_toto&, int)> dans l'argument.
    Bien évidement dans tous les cas il y aura copie de pointeur. Je te rappelle que le c++ passe par défaut les arguments par copie.

    Par contre je viens de voir dans un de tes liens l'utilisation de fonctions statiques avec un handler sur l'objet appelé:
    C'est sûrement pas la meilleur solution dans ton cas... Relis les liens.

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2003
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2003
    Messages : 17
    Par défaut
    La vache, je suis un peu largué :p

    Quand je remplace mon pValid->OnClick(this->test); par pValid->OnClick([this]{ this->test(); }); j'ai une erreur de "no matching function for call to test()" ce qui me semble logique puisque la fonction test est declarée avec un argument:
    void D_toto::test(W_button* caller)

    Si je met plutot pValid->OnClick([this]{ this->test(W_button* caller); }); alors je me prends une erreur "expected primary-expresion before '*' token"

    La je pige plus trop le concept

  7. #7
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    [this]{ this->test(); } est une expression, dont la valeur est une lambda qui prend this en référence dans son contexte de déclaration, n'a pas d'argument, et dont l'exécution consiste à l'évaluation de l'expression this->test().
    C'est en gros un pointeur de fonction.

    Si test est une fonction libre (non membre, donc), c'est probablement qu'il faut utiliser [this]{ test(*this); } ou [this]{ test(this); }. éventuellement, il faut rectifier la fonction.
    Si elle attend un argument autre que le fameux this, il faut trouver la valeur à lui donner.

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2003
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2003
    Messages : 17
    Par défaut
    Ben justement le truc c'est que ça peut être une fonction membre ou pas...

    Je suis de plus en plus largué

  9. #9
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    Le principal, c'est de renvoyer un foncteur, c'est à dire une l'instance d'une classe proposant l'opérateur ().

  10. #10
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2003
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2003
    Messages : 17
    Par défaut
    Je cherche a passer a un objet une fonction qui as des paramètres pour que l'objet puisse l'invoquer le cas echeant (c'est l'objet qui connais les parametres avec lesquel l'invoquer).
    Le problème c'est que la fonction a passer peut etre une fonction libre ou une fonction membre.
    libre dans le cas ou c'est l'utilisateur de ma lib qui l’écrit (il crée une instance de l'objet et lui passe la fonction de callback en paramètre) et membre dans le cas ou c'est un autre objet (une fenêtre de dialogue par exemple) qui crée cette instance de l'objet (un bouton dans la fenêtre par exemple)

  11. #11
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Alors il faut faire deux fonctions.

  12. #12
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2003
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2003
    Messages : 17
    Par défaut
    Et l'option fonctions statiques avec handler sur l'objet appelé du coup? non?
    https://isocpp.org/wiki/faq/pointers...fnptr-vs-fnptr

  13. #13
    Membre émérite
    Homme Profil pro
    Recherche du travail
    Inscrit en
    Août 2004
    Messages
    561
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Recherche du travail

    Informations forums :
    Inscription : Août 2004
    Messages : 561
    Par défaut
    Et l'option fonctions statiques avec handler sur l'objet appelé du coup? non?
    Don’t.

    Il vaut mieux un exemple que un long discour de comment utiliser les callbacks en c++
    http://pastebin.archlinux.fr/993723

  14. #14
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2003
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2003
    Messages : 17
    Par défaut
    Dans cet exemple la classe Button a besoin de savoir que le callback doit etre transmis a un widget

    Dans mon cas il faudrait ajouter une troisieme classe "dialog" qui créerais une instance de Button et lui donnerait une fonction membre de dialog comme fonction de callback

    En fait mon problème ressemble a ça:
    http://pastebin.archlinux.fr/993864

  15. #15
    Membre émérite
    Homme Profil pro
    Recherche du travail
    Inscrit en
    Août 2004
    Messages
    561
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Recherche du travail

    Informations forums :
    Inscription : Août 2004
    Messages : 561
    Par défaut
    Ton code n'a ni queue ni tête… Et je ne comprend pas ton problème.

  16. #16
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 639
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 639
    Par défaut
    Salut,

    Rien qu'à voir les première lignes de ton code à savoir
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class W_button
    {
        void (*onClick)(W_button* caller) = NULL;
     
        void W_button::OnClick(void (*function)(W_button* caller))
        {
            onClick = function;
        }
     
        W_button::call()
        {
            if(onClick) onClick(this);
        }
    }
    je me dis que tu devrais peut-être t'intéresser à la bibliothèque boost::signals2. Cette bibliothèque est en effet spécialement prévue pour générer un système de signaux (comprend: un moyen de dire "Il m'est arrivé quelque chose") et de slots (comprend un moyen de dire "Dis moi ce qui t'arrive, mon petit") qui sera beaucoup (mais alors beaucoup) plus simple pour toi à utiliser.

    Car, tel qu'il est là, ton bouton ne pourra signaler qu'à un seul élément qu'il lui est arrivé quelque chose. C'est parfois suffisant, mais... Que se passera-t-il lorsque deux éléments voudront être tenus au courent

    boost::signals2 fournit un tas de fonctionnalités connexes qui te seront sans doute utiles à un moment ou à un autre et, comme boost fait souvent figure de précurseur de ce que pourrait être "la prochaine norme", elle entre de toutes manières dans la catégorie des bibliothèques que tout bon développeur C++ devrait avoir en permanence dans "sa boite à outils"

    Cette bibliothèque en particulier présente, de plus, un avantage certain : elle est implémentée sous la forme dite "headers only" : "YAKA" s'assurer que les fichiers d'en-tête sont accessibles pour pouvoir l'utiliser (pas besoin de se faire ch...er à trouver "comment effectuer cette p..n d'édition de liens" )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  17. #17
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 760
    Par défaut
    @3dsman: J'ai oublié le paramètre (j'ai édité).
    Pour avoir plus d'explication sur les lambdas: http://en.cppreference.com/w/cpp/language/lambda.

    +1 pour boost.signal2

Discussions similaires

  1. Pointeur vers fonction membre
    Par poukill dans le forum C++
    Réponses: 20
    Dernier message: 17/01/2011, 10h08
  2. Utilité d'un pointeur vers une fonction ?
    Par Nasky dans le forum C
    Réponses: 10
    Dernier message: 20/03/2010, 19h54
  3. Pointeur vers une table dans une fonction
    Par Chatbour dans le forum Oracle
    Réponses: 2
    Dernier message: 03/05/2007, 12h28
  4. Réponses: 12
    Dernier message: 30/06/2006, 16h46
  5. Pointeur vers fonction
    Par flopaname dans le forum Langage
    Réponses: 3
    Dernier message: 23/06/2005, 15h46

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo