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

Langage C++ Discussion :

Problème d'appel de fonction dans une classe


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre émérite
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 79
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 122
    Par défaut Problème d'appel de fonction dans une classe
    Bonjour à toutes et à tous,

    Je suis désolé, mais la présentation de mon problème est un peu longue. Merci d'avance à ceux qui auront le courage d'aller jusqu'au bout.

    J'ai une classe de composant visuel :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Class Comp {
      ...
      void(*surAppui)(boolean);
      virtual boolean select(int x, int y);
    }
    Dans son constructeur, "surAppui = NULL". La fonction "select" est la suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Comp::select(int x, int y) {
      if (x et y dans la zone d'appui sur le composant)
        if (surAppui != NULL)
          surAppui(un boolean); // je renvois sur une fonction qui va faire ce que je veux
    }
    Cette fonction "surAppui" a le même mécanisme que le "onClick" dans Windows par exemple.

    J'ai créé des enfants de "Comp", par exemple "Bouton".

    Maintenant, à l'utilisation, j'instancie une classe "Bouton monBouton" et j'affecte à "surAppui" une fonction "quoiFaire" :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    monBouton.surAppui = &quoiFaire;
     
    void quoiFaire(boolean a) {
      //Exécute ce que j'ai envie de faire
    }
    Tout ce mécanisme fonctionne très bien, mais maintenant, je voudrais que ce mécanisme fonctionne à l'intérieur de mes classes.

    J'ai créé une classe "Page" descendant de "Comp" et utilisant "Bouton" :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class Page : public Comp {
      Bouton btn1;
      void chgtPage(boolean); // la fonction que veux associer à "surAppui"
      virtual boolean select(int x, int y); // modifie le comportement de la classe de base
      Page(les params qui vont bien) : btn1(ses params) {
        btn1.surAppui = &chgtPage // l'affectation de "chgtPage" à "surAppui"
      }
    }
    Alors que la construction de ce lien est semblable à celui qui fonctionne, c'est sur cette dernière instruction que le compilateur n'est pas d'accord. Il me renvoit l'erreur suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    MaClasse.cpp:13:24: error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function.  Say '&Page::chgtPage' [-fpermissive]
      btn1.surAppui = &chgtPage;
                            ^
    MaClasse.cpp:13:21: error: cannot convert 'void (Page::*)(boolean) {aka void (Page::*)(bool)}' to 'void (*)(boolean) {aka void (*)(bool)}' in assignment
      btn1.surAppui = &chgtPage;
                         ^
    Voilà, j'ai toujours été mal à l'aise avec ce genre de message. Si vous pouviez m'aider à trouver ce qui cloche.

    Merci de votre aide.

    Pierre

  2. #2
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 769
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 769
    Par défaut
    Ton compilateur te dit explicitement ce qui ne va pas

    En C++, cela s'appelle un pointeur de fonction membre ->

    En gros , il faut à chaque fois surspécifié ton pointeur void (*) (bool) (1) est différent de void (Page::*) (bool) (2)
    1. C'est un pointeur de fonction
    2. C'est un pointeur de fonction membre de la classe Page


    Et en plus de cela un pointeur de fonction membre a un appel très spécifique en .* ou ->* Par exemple (this->*pointer) (true).

  3. #3
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Une fonction membre de classe contient un paramètre caché : L'objet sur lequel cette fonction est appelée. Donc ce n'est pas directement compatible avec une fonction libre.
    Tu pourrais remplacer ton pointeur sur fonction libre par un pointeur sur fonction membre (avec une syntaxe assez horrible que je vais t'épargner, mais qui apparaît dans ton message d'erreur), mais alors tu ne pourrais plus utiliser de fonction libre.

    Déclaration

    Donc comment faire ? Il suffit de passer un objet qui peut encapsuler soit une fonction libre, soit une fonction membre, soit d'autres choses encore (en gros, tout ce qui peut être suivi par des parenthèses avec un booléen dedans, un appelle ça un foncteur en C++). Cet objet existe en standard en C++, mais il y a deux manières d'écrire le code :

    1/ Si la fonction à appeler est connue à la compilation (c'est généralement le cas d'un algorithme configurable), on utilise un template et on laisse faire le compilateur :

    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
    template<class Filtre>
    void afficheAvecFiltre(vector<int> const &v, Filtre filtre)
    {
        for(auto i : v)
        {
            if(filtre(i))
                cout << i << endl;
        }
    }
     
    bool estPair(int i)
    {
        return (i%2) == 0;
    }
     
    void f(vector<int> const &v)
    {
        afficheAvecFiltre(v, estPair);
    }
    2/ Si la fonction n'est pas connue à la compilation, et peut varier au cours du temps, on utilise std::function (on perd en perfs par rapport au cas précédent, mais on gagne en flexibilité) :

    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
    Class Comp {
      ...
      std::function<void(bool)> surAppui;
      virtual boolean select(int x, int y)
      {
         if (x et y dans la zone d'appui sur le composant)
           if (surAppui)
              surAppui(un boolean); // je renvois sur une fonction qui va faire ce que je veux  }
    }
     
    void quoiFaire(boolean a) {
      //Exécute ce que j'ai envie de faire
    }
     
    monBouton.surAppui = quoiFaire;
    Utilisation
    L'utilisation pour une simple fonction est triviale, comme indiqué plus haut. Pour une fonction membre, c'est un peu plus complexe. Il faut indiquer sur quel objet on va appliquer la fonction. Et on ne peut pas passer cet objet en paramètre, puisque notre seul paramètre au moment de l'appel est un booléen. Il faut donc qu'au moment où on crée notre foncteur, on lie à celui-ci l'objet sur lequel appeler la fonction. Il y a plusieurs manières d'y arriver, plusieurs écritures qui ont varié au cours du temps. Actuellement, je préfère utiliser les lambdas pour faire ça. Par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class Page : public Comp {
      Bouton btn1;
      void chgtPage(boolean); // la fonction que veux associer à "surAppui"
     
      Page(les params qui vont bien) : btn1(ses params) {
        btn1.surAppui = [this](bool b) {chgtPage(b); }; // l'affectation de "chgtPage" à "surAppui"
      }
    }
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  4. #4
    Membre émérite
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 79
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 122
    Par défaut
    Je vous remercie tous les deux pour ces réponses ... dont le niveau me dépasse. Je vais essayer de décortiquer tout cela, mais en attendant :

    @JolyLoic : j'ai simplement remplacé l'instruction :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    btn1.surAppui = &chgtPage;
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    btn1.surAppui = [this](boolean b) {chgtPage(b); };
    Apparemment, cela ne suffit pas car j'ai cette erreur :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    MaClasse.cpp: In constructor 'Page::Page(byte)':
     
    MaClasse.cpp:15:21: error: cannot convert 'Page::Page(byte)::__lambda0' to 'void (*)(boolean) {aka void (*)(bool)}' in assignment
     
      btn1.surAppui = [this](boolean a) {chgtPage(a); };
     
                         ^
    qui me dit qu'il ne peut pas convertir, mais pourquoi ?

    Merci de votre aide.

    Pierre

  5. #5
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Il faut faire les deux modifications indiquées pour que ça marche :
    - À l'endroit de l'appel, utiliser le lambda (ça, c'est bon, tu l'as fait)
    - Quand tu déclares ta variable surAppui, lui donner comme type std::function<void(bool)>, et non plus void(*)(boolean) (il faut avoir inclus le header <functional> pour ça)
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  6. #6
    Membre émérite
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 79
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 122
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    ... (il faut avoir inclus le header <functional> pour ça)
    j'ai donc ajouté à mon programme :

    et voilà la réponse :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    fatal error: functional: No such file or directory
    Apparemment, dans l'environnement "Arduino" dans lequel je travaille, ça ne doit pas exister

    Il doit bien y avoir moyen d'y échapper ?

    Cordialement.

    Pierre

  7. #7
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 769
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 769
    Par défaut
    JolyLoic te parle de C++YY (C++11, C++14, C++17, et plus) Est-ce que ton compilateur est compatible? (<- pourtant il semble l'être)

    Sinon, prends ma réponse parce que c'est du C++03 et elle passe partout. Par contre comme l'a dit JolyLoic, les pointeurs de fonction membre c'est super limité parce que tu dois toujours l'utiliser avec un objet de la classe en question.

    Et après soit trouve le moyen d'avoir un compilateur C++YY (et donc pouvoir coder des lambdas) soit essayes de faire la première solution avec les template de JolyLoic

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Appel d'une fonction dans une class
    Par pierre50 dans le forum Langage
    Réponses: 5
    Dernier message: 11/12/2008, 14h02
  2. Réponses: 1
    Dernier message: 25/10/2007, 18h04
  3. Réponses: 10
    Dernier message: 08/12/2006, 02h18
  4. Appel de fonction dans une classe
    Par saint-pere dans le forum Langage
    Réponses: 3
    Dernier message: 08/05/2006, 22h13
  5. Appel de fonction dans une classe
    Par Seth77 dans le forum Langage
    Réponses: 8
    Dernier message: 16/01/2006, 10h32

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