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 :

Un menu qui fait tout par lui même


Sujet :

C++

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Décembre 2013
    Messages : 31
    Points : 25
    Points
    25
    Par défaut Un menu qui fait tout par lui même
    Bonsoir,

    J'aimerais créer une classe Menu. Ce menu doit être écrit sur cout.

    Voici ma tentative :
    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
    class Menu
    {
    private:
        string *options;
     
        int nbOptions;
     
    public:
        Menu(string *options, int nbOptions);
     
        ~Menu(void);
     
        void show(void);
     
        char getUserChoice(void);
    };
    Les variables membres
    string *options : Est un tableau qui contient les options du menu
    int nbOptions : Définit le nombre d'options du menu
    Les méthodes membres
    Le constructeur : Initialise les variables membres.
    Le destructeur : Détruit le tableau de string.
    show : Affiche les différentes options. Avant chaque options, on rajoute le numéro de l'option par ordre d'arrivée.
    getUserChoice : Demande à l'utilisateur d'entrer un nombre. Ce nombre correspond au numéro de l'option qui lui est affiché sur le terminal.

    Mon soucis vient du fait qu'on est obligé d’appeler la méthode getUserChoice d'un objet Menu puis de faire un switch et en fonction du choix, effectuer une suite d'instructions.
    J'aimerais que tout soit géré par le menu.
    J'aimerais donc fournir au constructeur la liste des Actions(en bref un pointeur de fonction) associés aux différentes options.
    Mais utiliser des pointeurs de fonctions me déplaît. N'y a-t-il pas une autre solution, plus propre ?
    De plus, si j'ai bon souvenir, en C++, on essaie au possible d'éviter de jouer avec les pointeurs.. raison de plus ^^

    Merci

  2. #2
    Invité
    Invité(e)
    Par défaut
    Salut,

    tu as aussi la version polymorphique
    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 Action{
    };//abstraite
    class Action1:public Action{
    public:
        bool operator();
    };
     
    class Menu{
    protected:
        std::map<std::string, Action*> _map;
        void getUserchoice(){
            std::string userChoice=...;
            _map[userChoice]();//moyennant les verif
        }
    };
    Veux-tu que la durée de vie soit de celle de ta classe Menu ? Auquel cas, soit je copierais les instances depuis le constructeur, soit tu peux imaginer passer des fabriques (en template ou pas) puis dans ton constructeur récupérer une instance toute fraiche.

  3. #3
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,
    Citation Envoyé par valda117 Voir le message
    Bonsoir,

    J'aimerais créer une classe Menu. Ce menu doit être écrit sur cout.

    Voici ma tentative :
    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
    class Menu
    {
    private:
        string *options;
     
        int nbOptions;
     
    public:
        Menu(string *options, int nbOptions);
     
        ~Menu(void);
     
        void show(void);
     
        char getUserChoice(void);
    };
    Les variables membres
    string *options : Est un tableau qui contient les options du menu
    int nbOptions : Définit le nombre d'options du menu
    Bon, déjà, tu me vire ce string * options et ce int nbOptions que je ne saurais voir et tu remplace le tout par un std::vector<std::string> options; qui sera bien plus facile et bien plus sécurisant à l'usage
    Les méthodes membres
    Le constructeur : Initialise les variables membres.
    Le destructeur : Détruit le tableau de string.
    En utilisant un std::vector<std::string> comme indiqué, le destructeur ne devra plus rien faire.

    Tu as, en outre, oublié de prendre en compte la grande règle des trois (the "Big Three Rule") qui dit que, si tu dois redéfinir une des fonctions parmi le constructeur de copie, l'opérateur d'affectation ou le destructeur, il faut définir les trois.

    Cela nous emmène directement à nous poser une autre question : quelle sémantique vas-tu donner à ton menu

    Si tu lui donnes une sémantique de valeur, il faut définir l'opérateur d'affectation et le constructeur de copie de manière à ce qu'ils effectuent une copie en profondeur de ton string *, alors que si tu lui donnes une sémantique d'entité, il faut faire en sorte d'interdire purement et simplement la copie et l'affectation

    Ce sont autant d'écueils que tu éviterais en utilisant std::vector comme je te le conseille
    show : Affiche les différentes options. Avant chaque options, on rajoute le numéro de l'option par ordre d'arrivée.
    Ca, ca n'a rien de bien compliqué
    getUserChoice : Demande à l'utilisateur d'entrer un nombre. Ce nombre correspond au numéro de l'option qui lui est affiché sur le terminal.
    Cela parrait logique
    Mon soucis vient du fait qu'on est obligé d’appeler la méthode getUserChoice d'un objet Menu puis de faire un switch et en fonction du choix, effectuer une suite d'instructions.
    C'est le but d'un menu, non
    J'aimerais que tout soit géré par le menu.
    J'aimerais donc fournir au constructeur la liste des Actions(en bref un pointeur de fonction) associés aux différentes options.
    Très mauvaise idée!!!

    Tu dois veiller à respecter le principe de la responsabilité unique.

    La responsabilité de ton menu est de donner le choix à l'utilisateur. Exprimée sous cette forme, on peut admettre la possibilité pour ton menu de vérifier le fait que l'utilisateur introduit une valeur cohérente et de la renvoyer, mais ce n'est a priori pas au menu de choisir ce qui doit être fait en fonction du choix de l'utilisateur.

    Ce qu'il est possible de faire --c'est d'ailleurs en très gros le principe suivi par les bibliothèque IHM -- c'est de faire en sorte que le menu émette un signal qui indique le choix de l'utilisateur au moment où l'utilisateur fait son choix et d'aller connecter certains comportements (on appelle cela des slot ) à ce signal pour qu'ils soient exécutés lorsque le signal est émis.

    Il échoit alors à ces comportements de décider s'il doivent réagir ou non au signal en fonction du choix de l'utilisateur. Mais, en gros, cela signifie que tu n'as plus qu'un seul if qui teste une valeur spécifique pour chaque slot (et qui ne fait strictement rien si la valeur obtenue de la part du signal ne correspond pas)

    Cependant, cette possibilité ne me semble pas forcément adaptée à une utilisation en mode console pour la simple et bonne raison que cela ferait de ton menu la "partie centrale" de ton application : la partie au départ de laquelle tout part et où tout arrive.

    Il est -- à mon sens -- beaucoup plus intéressant d'envisager cette option lorsque tu travailles sur une IHM où l'application doit pouvoir fonctionner de manière plus ou moins indépendante de la "lubie" de l'utilisateur d'aller cliquer sur un menu particulier

    En outre, le système de signaux et de slot n'existe pas en standard, et il faudrait te tourner vers une bibliothèque tierce -- comme boost::signals2 par exemple -- si tu ne veux pas commencer à (mal) réinventer la roue.

    A voir le niveau de ta question, j'aurais vraiment tendance à te conseiller d'attendre un tout petit peu d'avoir assimilé les base du C++ avant de te lancer dans l'aventure (sans vouloir t'offenser )
    Mais utiliser des pointeurs de fonctions me déplaît.
    Tu ne te gènes pourtant pas pour utiliser un pointeur pour représenter tes options!

    Mais tu as tout à fait raison : il est largement préférable d'en éviter l'usage autant que faire se peut
    N'y a-t-il pas une autre solution, plus propre ?
    La solution la plus propre, je viens de te la donner : changer radicalement ton fusil d'épaule et soit décider de gérer le choix de l'utilisateur en dehors du menu, soit décider de travailler avec un système de signaux et de slots

    Sinon, il existe bien des solutions plus propres, mais elles sont de niveau bien plus élevé et nécessitent le recours à des possibilités qui ne sont apparues qu'avec la norme C++11.

    Tu pourrais, par exemple, utiliser la classe std::function au lieu d'un pointeur de fonction.

    Tu pourrais alors envisager de créer une structure proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct MenuItem{
        MenuItem(std::string const & description):description(description){}
        std::string description;
        std::function<void> callee;
    };
    qui servirait dans une map dont la clé serait un entier (de préférence non signé).

    Cela prendrait la forme de
    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
    class Menu{
    public:
        Menu(){
            options_.insert(std::make_pair(options_.size()+1,MenuItem("option1")));
            options_.insert(std::make_pair(options_.size()+1,MenuItem("option2")));
            options_.insert(std::make_pair(options_.size()+1,MenuItem("option3")));
            options_.insert(std::make_pair(options_.size()+1,MenuItem("option4")));
            options_.insert(std::make_pair(options_.size()+1,MenuItem("quitter")));
        }
        void connect(size_t optNumber,std::function<void()> & fun){
            auto it = options_.find(optNumber);
            if(it == options_.end()){
                std::cout<<"option doesn't exists, abording"<<std::endl;
                return ;
            }
            it->second.callee.swap(fun);
        }
        bool run(){
            for(auto & it : options_){
                std::cout<<it.first<<" "<<it.second.description<<std::endl;
     
            }
            std::cout<<std::endl<<"Votre choix ?";
            size_t choice;
            std::cin>>choice;
            if(choice < options_.size()){
                auto  it = options_.find(choice);
                it->second.callee();
                return true;
            }
            std::cout<<"good bye, see you soon"<<std::endl;
            return false;
     
        }
    private:
        std::map<size_t, MenuItem> options_;
    };
    et tu pourrais l'utiliser sous une forme proche de
    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
    void foo1(){
        std::cout<<"foo1"<<std::endl;
    }
    void foo2(){
        std::cout<<"foo2"<<std::endl;
    }
    void foo3(){
        std::cout<<"foo3"<<std::endl;
    }
    void foo4(){
        std::cout<<"foo4"<<std::endl;
    }
    int main(){
        Menu menu;
        std::function<void()> fun(&foo1);
        menu.connect(1,fun);
        fun= foo2;
        menu.connect(2,fun);
        fun= foo3;
        menu.connect(3,fun);
        fun= foo4;
        menu.connect(4,fun);
        while(menu.run());
        return 0;
    }
    Evidemment, il y a de nombreuses possibilités basées sur les principes énoncés ici et le code est très certainement perfectible (entre autres, pour sécuriser l'introduction effectuée par l'utilisateur), mais le gros du principe reste globalement le même
    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

Discussions similaires

  1. Réponses: 23
    Dernier message: 23/10/2011, 08h20
  2. Menu qui fait des misères
    Par kaiser59 dans le forum Mise en page CSS
    Réponses: 5
    Dernier message: 08/12/2006, 20h06
  3. [POO] Une classe qui fait tout ?
    Par Nasky dans le forum Langage
    Réponses: 23
    Dernier message: 26/05/2006, 20h02
  4. Clé Registre et copie du programme par lui même
    Par True Cluster dans le forum Windows
    Réponses: 5
    Dernier message: 13/03/2006, 13h59
  5. Un retour à la ligne qui fait tout foirer !!!!
    Par sami_c dans le forum Balisage (X)HTML et validation W3C
    Réponses: 8
    Dernier message: 27/02/2006, 10h20

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