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 :

lancer une fonction membre grace à une chaine de caracteres


Sujet :

C++

  1. #1
    Candidat au Club
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2007
    Messages : 15
    Points : 2
    Points
    2
    Par défaut lancer une fonction membre grace à une chaine de caracteres
    Bonjour,

    Mon but est de créer une classe Interpreter qui gère l'interprétation d'un message sous forme de chaine de caractère : KEYWORD1 param1.1 param1.2 ... KEYWORD2 param2.1 param2.2 ...

    Pour ce faire, je commence par initialiser une std::map en associant un pointeur vers une fonction de traitement membre à un mot clef.

    Mes fonctions de traitement sont toutes construites sur le même modèle :
    bool myFunction(std::vector<std::string> params).

    J'ai enfin une fonction readMessage(const std::string& keyword,std::vector<std::string>) qui lancera la bonne fonction de traitement en fonction du mot clef.

    D'autres fonctions gèrent le découpage de la chaine en entrée mais là n'est pas le problème.
    En fait le compilo me jète lorsque je remplis ma map en me disant que je ne peux pas referencer une fonction membre liée :
    error C2276: '&': opération non conforme sur l'expression d'une fonction membre liée.

    Voilà mon code :

    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
    30
    31
    32
     
    typedef bool (*read_func)(std::vector<std::string>);
     
    class Interpreter
    {
    private:
       std::map<std::string,read_func> funcTab;
     
    public:
       void init();
       bool readMessage(const std::string& keyword, std::vector<std::string> params);
       bool readFunc1(std::vector<std::string> params);
       bool readFunc2(std::vector<std::string> params);
       //...
    };
     
    void Interpreter::init()
    {
       funcTab["KEY1"] = &readFunc1;
       funcTab["KEY2"] = &readFunc2;
       //...
    }
     
    bool Interpreter::readMessage(const std::string& keyword,std::vector<std::string> params)
    {
       std::map<std::string,read_func>::const_iterator element = funcTab.find(keyword);
     
       if (element == funcTab.end())
          return false;
       else
          return (iter->second)(params);
    }
    Si quelqu'un a une idée ? Merci d'avance

  2. #2
    Membre éclairé Avatar de mchk0123
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    816
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 816
    Points : 844
    Points
    844
    Par défaut
    funcTab["KEY1"] = &readFunc1;
    funcTab["KEY2"] = &readFunc2;
    Essaye :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
       funcTab["KEY1"] = &Interpreter::readFunc1;
       funcTab["KEY2"] = &Interpreter::readFunc2;
    Car en C++ on ne peut avoir que des pointeurs sur membres de classes et non pas des pointeurs sur instances de classes.

  3. #3
    Candidat au Club
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2007
    Messages : 15
    Points : 2
    Points
    2
    Par défaut
    Après modif j'ai une nouvelle erreur :

    error C2440: '=' : impossible de convertir de 'bool (__thiscall Interpreter::* )(std::vector<_Ty>)' en 'bool (__cdecl *)(std::vector<_Ty>)'
    1> with
    1> [
    1> _Ty=std::string
    1> ]
    1> Aucun contexte dans lequel cette conversion est possible

  4. #4
    Membre éclairé Avatar de mchk0123
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    816
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 816
    Points : 844
    Points
    844
    Par défaut
    Encore une chose :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    typedef bool (*read_func)(std::vector<std::string>,void*);
    Déclare un type "pointeur de fonction", ce qui est incompatible avec "&Interpreter::readFunc1" qui est de type "pointeur sur fonction de classe".

    Voilà pourquoi tu as ce message d'erreur du compilateur.

  5. #5
    Candidat au Club
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2007
    Messages : 15
    Points : 2
    Points
    2
    Par défaut
    Ok c'est bien ça

    je remplace

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    typedef bool (*read_func)(std::vector<std::string>);
    par

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    class Interpreter;
     
    typedef bool (Interpreter::*read_func)(std::vector<std::string>);
    et ça marche nickel !
    Merci beaucoup.

  6. #6
    Membre éclairé Avatar de mchk0123
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    816
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 816
    Points : 844
    Points
    844
    Par défaut
    Une dernière chose, pour effectuer un appel à la fonction il te faudra :
    - un pointeur sur fonction membre de classe
    - un pointeur sur une instance de la classe
    - utiliser l'opérateur "->*"

    Par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Interpreter *obj_ptr;
    Interpreter::*read_func func_ptr;
    
    obj_ptr = new Interpreter;
    obj_ptr->init();
    func_ptr = obj_ptr->funcTab["KEY1];
    
    obj_ptr->*func_ptr(...);

  7. #7
    Candidat au Club
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2007
    Messages : 15
    Points : 2
    Points
    2
    Par défaut
    Un dernier souci.
    Dans ma fonction readMessage, lorsque j'appelle la fonction déterminée grâce au mot clef voilà ce qui se produit :

    error C2064: le terme ne correspond pas à une fonction qui prend 1 arguments

    Qu'est-ce qui ne va pas ?

    Edit : Je me demandais encore une chose. En définissant read_func comme un pointeur sur une fonction d'Interpreter est-ce que je pourrais le réutiliser sur des fonctions d'une classe fille ? Si ce n'est pas le cas comment faire en sorte que si ?

  8. #8
    Membre éclairé Avatar de mchk0123
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    816
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 816
    Points : 844
    Points
    844
    Par défaut
    Le pointeur de fonction récupéré par iter-> est un pointeur sur membre de classe. Or ce pointeur ne peut pas être appellé directement, il faut que établisse une liaison entre ce pointeur et une instance de tas classe.
    (pour savoir comment faire regarde mon code ci-dessus).

    Lorsque que l'on appelle une fonction d'instance, il y a passage implicite d'un argument supplémentaire "this". C'est pour ça que tu as ce message d'erreur.

  9. #9
    Candidat au Club
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2007
    Messages : 15
    Points : 2
    Points
    2
    Par défaut
    this ne représente pas une instance de la classe ? C'est ce que je fais en appelant directement ma fonction de traitement : this->readFunc(...). Alors pourquoi this->*(iter->second)(...) ne fonctionne pas ?

    Lorsque je crée une instance d'Interpreter et que je l'initialise avec init(), ce que je stocke dans ma map ne sont pas des pointeurs vers des fonctions de cette instance ?
    Dès lors je capte pas pourquoi ça foire quand je les appelle.

    gnarf, j'y capte de moins en moins

    Edit : Tu as un avis sur l'utilisation de read_func dans les classes héritées d'Interpreter ?

  10. #10
    Membre éclairé Avatar de mchk0123
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    816
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 816
    Points : 844
    Points
    844
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    this->readFunc(...). Alors pourquoi this->*(iter->second)(...)
    Avec ce style d'écriture, tu risque d'avoir un problème de priorité entre les opérateur ->* () etc.
    Essaye de mettre des parenthèses pour forcer l'ordre de priorité (précédence).

    Exemple de pb. de précédence plus parlant (entre les opérateurs + et *) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    printf("%d\n", 1 + 2 * 3);
    printf("%d\n", (1 + 2) * 3);
    Ou mieux utilise une variable intermédiaire pour stocker ton pointeur de fonction.

  11. #11
    Candidat au Club
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2007
    Messages : 15
    Points : 2
    Points
    2
    Par défaut
    J'ai déjà essayé plusieurs écriture ainsi que le passage par une variable mais rien n'y fait. De plus j'ai ce souci de type de pointeur sur fonction membre (read_func) qui ne peux pas être hérité.

    Bref je vais exprimer mes objectifs d'une autre façon parce que j'ai l'impression que je m'y prend mal.

    Donc mon Interpreter c'est le mécanisme qui permet de commander un objet en lui passant une chaine de caractère de la forme : COMMAND param1 param2 ... Elle réalise une association entre un mot-clef et une fonction de l'objet commandé.

    J'ai par exemple un Server qui a la capacité de recevoir et d'envoyer des données à travers le réseau. Je veux lui donner la capacité d'interpréter les données qu'il reçoit sous forme de commandes.

    Je crée alors une nouvelle classe ServerCom qui hérite de Server et d'Interpreter.
    A son initialisation j'associe des mots clefs aux fonctions de Server.
    Lorsque le ServerCom reçoit un message il peut alors l'interpréter comme une série de commandes et lancer les traitements correspondants.

    De plus si je dois ajouter de nouvelles fonctionnalités à mon ServerCom, je veux pouvoir le spécialiser en classes filles et réaliser de nouvelles associations mot-clef/fonctions.

    Voilà. Ca ne me parait être quelque chose d'irréalisable. Mais je manque un peu de méthode et d'expérience. Alors si vous avez un avis dessus ou des liens, n'hésitez pas à poster.

    Merci

  12. #12
    Membre éclairé Avatar de mchk0123
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    816
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 816
    Points : 844
    Points
    844
    Par défaut
    Je comprend un peu mieux ce que tu veux faire.

    Effectivement si tu t'engage dans un essai d'héritage de pointeur de membres de classes tu n'est pas au bout de tes surprises et de tes peines.

    Déjà si tu veux pouvoir créer de nouvelles classes en étendant le jeu de commande, tu peut trés bien hériter d'une classe Server de base, mais il faut que tes classes Server & Interpreter se comporte comme une classe virtuelle.

    Autrement dit tu met en place tout le mécanisme de transformation chaine de commande -> appel de méthode, mais de manière complètement abstraite sans savoir à l'avance quel type de server & d'interpreter concret sera utilisé.

    Pour ça tu utilise partout des références ou des pointeurs sur ces classes mère de base. Quand tu appellera le mécanisme de transformation à partir de tes classes filles, C++ convertira ses pointeurs / références en type "classes mères".

    Ca c'est la théorie, maintenant si tu mettais le bout de code qui pose pb. ça pourrait aider un peu.

  13. #13
    Candidat au Club
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2007
    Messages : 15
    Points : 2
    Points
    2
    Par défaut
    Voila le code qui correspondrait à l'exemple du haut :
    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
     
    class Interpreter;
     
    typedef (Interpreter::*read_func)(std::vector<std::string>);
     
    class Interpreter
    {
    protected:
      std::map<std::string,read_func> funcTab; // tableau associant mot clef et fonctions membres
     
      virtual void initFuncTab() = 0; // initialisation du tableau d'association
     
      // lance la fonction de traitement correspondant à la commande
      bool readCommand(const std::string& command, std::vector<std::string> params) {
        std::map<std::string,read_func>::const_iterator element = funcTab.find(command);
     
        if ( element != funcTab.end() )
          return (element->second)(params);
     
        return false;
      }
     
    public:
      Interpreter() { initFuncTab(); }
      ~Interpreter() {}
    };
     
    class Server
    {
    protected:
      virtual void readData(std::string data) = 0; //lit les donnes reçues
      //virtual fonction_de_traitement1
      //virtual fonction_de_traitement2
      // ...
     
    public:
      Server() {}
      ~Server() {}
    };
     
    class ServerCom : public Server, Interpreter
    {
    protected:
      //lance la fonction de traitement 1 avec des paramètres sous forme de chaine de caractères
      bool launchTraitement1(std::vector<std::string> params) {
        //Si params corrects
        //lancer fonction_de_traitement1( param1, param2 ...) et retourner true;
        //Sinon retourner false
      }
     
      bool launchTraitement2(std::vector<std::string> params) { // ... }
      // ...
     
      void readData(std::string data) {
        std::vector<std::string> params;
        std::string command;
     
        //parser data et stocker dans params
        //retirer le premier element de params et le stocker dans command
     
        if (!readCommand(command,params))
        {
          //Traiter l'erreur
        }
     
      }
     
      virtual void initFuncTab() {
        funcTab["COMMAND1"] = &ServerCom::launchTraitement1;
        funcTab["COMMAND2"] = &ServerCom::launchTraitement2;
        // ...
      }
     
    public:
      ServerCom() {}
      ~ServerCom() {}
    }
    Je n'ai pas détaillé Server. Ca aurait pu être n'importe quel autre classe recevant des données sous forme de chaine de caractères.

    Ce qui pose problème c'est
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return (element->second)(params);
    dans la fonction readCommand de la classe Interpreter avec l'erreur de nombre d'argument

    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    funcTab["COMMAND1"] = &ServerCom::launchTraitement1;
    funcTab["COMMAND2"] = &ServerCom::launchTraitement2;
    // ...
    dans la fonction initFuncTab de la classe ServerCom où c'est le type Interpreter::* qui est attendu au lieu de ServerCom::* .

    J'attends vos conseils. Merci

  14. #14
    Membre éclairé Avatar de mchk0123
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    816
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 816
    Points : 844
    Points
    844
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
      // lance la fonction de traitement correspondant à la commande
      bool readCommand(const std::string& command, std::vector<std::string> params) {
        std::map<std::string,read_func>::const_iterator element = funcTab.find(command);
     
        if ( element != funcTab.end() )
          return (element->second)(params);
     
        return false;
      }
    A remplacer par :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
      // lance la fonction de traitement correspondant à la commande
      bool readCommand(const Server &server, const std::string& command, std::vector<std::string> params) {
        std::map<std::string,read_func>::const_iterator element = funcTab.find(command);
        read_func *func;
     
        if ( element != funcTab.end() ) {
          func = element->second;
          return (&server)->*func(params);
        }
     
        return false;
      }
    Puisque que Interpreter doit travailler sur des fonctions de la classe Server (et fonctions de classes héritées de Server).

    + Autres modifications à faire sur le remplissage de funcTab.

  15. #15
    Candidat au Club
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2007
    Messages : 15
    Points : 2
    Points
    2
    Par défaut
    Arf même en appelant la fonction avec une référence d'un Server j'obtiens la même erreur de nombre d'argument.

    Pour l'initialisation de la map je m'en tire avec des warnings en utilisant un static_cast :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    functab["KEY"] = static_cast<read_func>(&ServerCom::launchTraitement);
    J'ai aussi pensé à modifier le type read_func en pointeur vers une fonction de Server plutôt que Interpreter. Mais rien n'y fait.

  16. #16
    Membre chevronné
    Avatar de poukill
    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 155
    Points : 2 107
    Points
    2 107
    Par défaut
    Si je vois bien ce que tu veux faire, ça correspond exactement à une combinaison du pattern Command et Fabrique :
    Fabrique : tu as une map pour associer une chaine de caractère à une commande.
    A la place de Command, tu pourrais utiliser boost::function, c'est un choix. J'avais préféré Command...

  17. #17
    Candidat au Club
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2007
    Messages : 15
    Points : 2
    Points
    2
    Par défaut
    Dans mes recherches j'ai souvent trouvé des références à boost::function. C'est sûr que manipuler des objets function plutôt que des pointeurs sur fonction membre rendrait les choses plus faciles... et moins moches. Mais je pensais pouvoir m'en sortir sans installer de bibliothèque supplémentaire.

    Je n'ai jamais utilisé de patterns mais il faut vraiment que je m'y mette. Ca devient lourd de partir de rien alors que je pourrais utiliser des "pièces détachées" performantes.

    Je vais m'intéresser à Command et Fabrique. Alors si vous avez de bonnes références vers ces patterns et leur implémentation en C++ n'hésitez pas à poster. Merci

  18. #18
    Membre chevronné
    Avatar de poukill
    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 155
    Points : 2 107
    Points
    2 107
    Par défaut
    Je te donne un lien pour les design pattern sur DVP.com -> ICI
    Si tu veux en savoir plus, je te conseille le bouquin Design Pattern, des éditions tête la Première, il est vraiment bien!

  19. #19
    Membre éclairé Avatar de mchk0123
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    816
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 816
    Points : 844
    Points
    844
    Par défaut
    Personnellement je préfère la version originale (bien qu'en Anglais).

    Mais bon c'est juste une affaire de goût personnel.

  20. #20
    Membre chevronné
    Avatar de poukill
    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 155
    Points : 2 107
    Points
    2 107
    Par défaut
    Oui après ça dépend de ta maîtrise de l'anglais...

Discussions similaires

  1. [Free Pascal] Enregistrer une fonction callback depuis une fonction membre
    Par EpiTouille dans le forum Free Pascal
    Réponses: 3
    Dernier message: 11/03/2015, 11h11
  2. Thread d'une fonction membre dans une fonction membre
    Par virtual_bug dans le forum Threads & Processus
    Réponses: 2
    Dernier message: 19/03/2014, 21h49
  3. Réponses: 5
    Dernier message: 29/06/2006, 17h23
  4. Réponses: 3
    Dernier message: 29/04/2006, 13h02
  5. Thread avec une fonction membre d'une classe
    Par SteelBox dans le forum Windows
    Réponses: 6
    Dernier message: 01/03/2004, 01h15

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