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 :

alternative à switch


Sujet :

C++

  1. #1
    Membre à l'essai
    Profil pro
    E/C
    Inscrit en
    Février 2006
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : E/C

    Informations forums :
    Inscription : Février 2006
    Messages : 31
    Points : 22
    Points
    22
    Par défaut alternative à switch
    Bonjour,
    Par curiosité, je m'intéressais au cas du switch, qui ne permet de traiter que des constantes. Comment avoir un switch qui puisse déclencher sur des variables ? Comme application triviale, on peut penser par exemple à un choix utilisateur qui soit personnalisable (entrée clavier ou autre) via un fichier de config, chargé au runtime.

    J'ai commencé à réfléchir à la question, et suis arrivé rapidement à une classe "templatée" sur le type de variable, et qui mémorise dans un vector des pointeurs de fonction. Ca marche bien, sauf que toutes les fonctions d'appel doivent avoir la même signature (et qui de plus est figée à la compilation, sauf à vouloir).

    J'ai cherché un peu sur le www, mais j'ai pas trouvé grand chose sur ce thème. Quelqu'un aurait-il des pointeurs là dessus, ou une opinion personnelle sur la question (voire même une implémentation ?)

    Merci.

  2. #2
    Membre chevronné Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 043
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 043
    Points : 2 234
    Points
    2 234
    Par défaut
    Bonjour,
    Ta demande n'est pas vraiment clair, un switch, une constante, une variable...

    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
     
    int i; // ma variable
    const int j; // ma constante
     
    //switch sur variable
    switch i
    {
    cas 1:
    cas 2:
    }
     
    //switch sur constant ( Inutile )
    switch j
    {
    cas 1:
    cas 2:
    }
    Homer J. Simpson


  3. #3
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    Bonjour sebkramm

    Je ne suis pas sur non plus d'avoir bien compris la question donc j'espère ne pas répondre à côté...

    A priori, ce que tu souhaites, c'est pouvoir ajouter dynamiquement des "case xxx:" et les fonctions associées ? Dans ce cas, ça pourrait ressembler à un design pattern Chaîne de responsabilité : une liste de module traitent une requête un par un ; le premier qui accepte la requête effectue le traitement adéquate et arrête la propagation de la requêt.

    Si ce n'est pas ce que tu recherches, peux tu préciser ta question ?

  4. #4
    Membre à l'essai
    Profil pro
    E/C
    Inscrit en
    Février 2006
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : E/C

    Informations forums :
    Inscription : Février 2006
    Messages : 31
    Points : 22
    Points
    22
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    Je ne suis pas sur non plus d'avoir bien compris la question donc j'espère ne pas répondre à côté...
    Oups, désolé pour l'imprécision.
    Donc, le "problème" du switch, c'est qu'on ne peut déclencher que sur des constantes
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    switch( ma_var )
    {
       case 1:
    //                action 1
       break;
       case 2:
    //                action 2
       break;
    }
    Eventuellement, on peut avoir des identifiants à la place, mais il faut qu'ils soient
    • const
    • assimilable à un int (char ou autre)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    const int v1 = 1;
    const int v2 = 2;
    switch( ma_var )
    {
       case v1:
    //                action 1
       break;
       case v2:
    //                action 2
       break;
    }
    Au final, ça change pas grand chose...

    La question que je me pose c'est : quelle est la meilleure approche quand on souhaite déclencher sur des variables pour v1, v2 (donc, sans le const), de plus pour des types quelconques.

    (et évidemment sans une illisible chaine "if/else/if/else/if/else/...)

    Par exemple, si je veux faire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    string s;
    ...
    switch( s )
    {
       case "aaa":
    //                 action 1
       break;
     
       case "bbb":
    //                 action 2
       break;
    }
    Voire même:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    TypeQuelconque s,v1,v2; // ayant "==" défini, quand même...
    ...
    switch( s )
    {
       case v1:
    //                 action 1
       break;
     
       case v2:
    //                 action 2
       break;
    }
    Alors j'ai cru voir quelque part (retrouve plus où...) que quelqu'un proposait un DP strategy dans ce cas. Mais d'une part je suis pas très sur de voir en pratique comment on ferait, et d'autre part, ça me semble être un bazooka pour tuer une mouche.

    Je suis arrivé à un truc qui permette de faire ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    SWITCH<string> my_sw;
     
    my_sw.Add( "aa", f1 ); // f1, f2: les fonctions à appeler
    my_sw.Add( "bb", f2 ); 
     
    // traitement de la string s:
    my_sw.Process( s );
    La classe (simplifiée, le cas du default est aussi prévu):
    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
     
    template < class A > class SWITCH
    {
            public:
                void Process( A a )
                {
                    for( int i=0; i<v1.size(); i++ )
                        if( a == v1[i] )
                            v2[i]();
                }
     
                void Add( A a,  void(*f)() )
                {
                    v1.push_back( a );
                    v2.push_back( f );
                }
     
            private:
                std::vector<A> v1;
                std::vector< void(*)() > v2;
    }
    Ce qui me gène, c'est que, sauf erreur de ma part, on ne peut pas "passer" la signature des fonctions à appeler au template, donc c'est figé pour tout les types de switch d'une unité de compilation donnée. Sauf à utiliser des tricks de préprocesseurs à coups de #ifdef, peut-être...

    Voyez-vous d'autres approches, avec quels avantages/inconvénients ?

  5. #5
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Points : 4 732
    Points
    4 732
    Par défaut
    Quid d'une simple map avec un {std,boost}::function du bon type pour gérer l'argument ?
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  6. #6
    Membre à l'essai
    Profil pro
    E/C
    Inscrit en
    Février 2006
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : E/C

    Informations forums :
    Inscription : Février 2006
    Messages : 31
    Points : 22
    Points
    22
    Par défaut
    Citation Envoyé par Davidbrcz Voir le message
    Quid d'une simple map avec un {std,boost}::function du bon type pour gérer l'argument ?
    Oui, j'ai pensé à utiliser une map au début, mais comment tu gères le cas du default ? (je l'ai prévu dans ma classe, mais pas mis ici pour pas alourdir)

    Et le boost::function, merci, je connaissais pas, ça à l'air d'être justement ce qui me manque pour wrapper une signature de fonction, je vais essayer ça.

  7. #7
    Invité
    Invité(e)
    Par défaut
    Bonjour,
    Sur le principe, un switch est justement fait pour permettre des branchements directs. C'est tout de même assez délicat de prévoir une éventuelle possibilité de la part de l'utilisateur de pouvoir entrer une commande qui n'a pas été prévue.
    Autrement, si toutes les commandes on été prévues, là, pas de problème et la syntaxe à préférer est un enum avec un tableau associé. Je me souviens avoir vu un article de FAQ qui le détaille.

  8. #8
    Membre à l'essai
    Profil pro
    E/C
    Inscrit en
    Février 2006
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : E/C

    Informations forums :
    Inscription : Février 2006
    Messages : 31
    Points : 22
    Points
    22
    Par défaut
    Citation Envoyé par Pierre Dolez Voir le message
    C'est tout de même assez délicat de prévoir une éventuelle possibilité de la part de l'utilisateur de pouvoir entrer une commande qui n'a pas été prévue.
    Non, ce n'est pas ça. Le cas de la saisie utilisateur est juste un exemple pour montrer un cas où ça pourrait être utile. Pour rester la dessus, il ne s'agit pas de prévoir quelque chose qui n'a pas été prévu (!), mais de pouvoir dynamiquement changer les valeurs déclenchant le branchement. Par exemple ici, on peut penser à un système de raccourci personnalisable par l'utilisateur via un fichier de config. On veut modifier les raccourcis par défaut? Hop, on charge le fichier de config kivabien, dans les variables utilisées dans les 'case'.

    En l'occurrence, ca serait donc plutot:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    switch( saisie_clavier )
    {
       case tab[ACTION_1]:
    //               action 1
       break;
       case tab[ACTION_2]:
    //               action 2
       break;
      ...
    }
    avec ACTION_1, ACTION_2, .. étant des enumerator.
    Alors oui, on pourrait utiliser un map[enum] contenant des fonctions, mais quid du default ? (et nous savons tous qu'il faut TOUJOURS traiter le default...). Et puis on ne pourrait pas faire des branchements "glissants" comme avec le switch classique (omission du break).

    D'une façon plus générale, ce point technique est une simple interrogation de ma part. Je sais que le branchement sur des constantes 'int' seulement est lié à l'implémentation sous jacente héritée du C. Pour moi, on a ici un point faible du C++, il est illogique que dans un langage à objets, la structure de contrôle switch/case ne puisse pas s'utiliser avec des types quelconques, à partir du moment où ils sont dotés de l'opérateur ==.

    Pour l'histoire des raccourcis, ça se fait dans certaines GUI, et donc on doit aussi pouvoir faire ça sans switch. Mais je ne veux pas focaliser sur cet exemple, c'est plus l'idée générale qui m'intéresse.

    Après, si quelqu'un me démontre que ce type de situation n'existe pas... (ou correspond à une mauvaise analyse, ou peut être contourné d'une façon plus élégante, ou...)

  9. #9
    Invité
    Invité(e)
    Par défaut
    Il y a eu dernièrement un sujet qui traitait justement de ce problème (je ne l'ai pas retrouvé).
    Sur le plan de la logique pure, j'ai un peu de mal à imaginer des paramètres de case en variables non const. C'est concevable avec des programmes interprétés mais à mon avis, pas avec des programmes compilés. Il me parait impossible de créer l'éditeur de lien (link).
    Donc, je ne considère pas que ce soit un défaut, bien au contraire, dans le mesure où cela apporte une sécurité.
    Dans le cas qui nous intéresse, votre exemple avec Action_1 etc., la case default n'est même pas utile, puis qu'elle mettrait en évidence une faute d'écriture.
    La case défaut est faite pour "Traiter les autres cas" et non éviter un plantage.
    La solution de l'enum est parfaite pour ce type de cas, et l'utilisation d'un tableau de chaines offre une facilité pour passer directement de la lecture de la commande à l'adresse de l'action à exécuter.
    J'utilise cette technique avec 78 case (je les ai comptées dernièrement), mais seulement un enum et pas de tableau.

    Voila le lien.
    http://www.developpez.net/forums/d99...ghlight=switch
    Vous constaterez que j'ai obtenu de nombreuses mauvaises notes sur ce sujet, étais-je à côté de la question ? ais-je essayé à tord de faire profiter d'autres de méthodes que j'avais testé et que j'utilisais ? je ne sais pas. Je suppose que ceux qui se sont manifestés ainsi et anonymement ont l'expérience et les compétences pour me juger d'après des réponses.

    Cordialement.
    Dernière modification par Invité ; 05/11/2010 à 18h20.

  10. #10
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Je ne vois vraiment pas le rapport avec l'éditeur de lien

    Le switch peut intervenir dans différents contextes permettant son remplacement éventuel par d'autres approches (automate=>DP state par expl).

    Dans le cas que tu présentes (une saisie clavier <-> une action), l'approche est peut être de d'abord interpréter ta saisie clavier en terme d'évènement possible de ton soft et de l'injecter dans le switch :
    1/ saisie clavier -> une_saisie
    2/ conversion une_saisie -> enum (par expl)
    3/ switch(enum) ou map<enum, DP Commande (std/boost::function trivialement)>, le default peut être traité dans la conversion saisie/enum => actions adéquates.

  11. #11
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Points : 4 732
    Points
    4 732
    Par défaut
    Oui, j'ai pensé à utiliser une map au début, mais comment tu gères le cas du default ? (je l'ai prévu dans ma classe, mais pas mis ici pour pas alourdir)

    Et le boost::function, merci, je connaissais pas, ça à l'air d'être justement ce qui me manque pour wrapper une signature de fonction, je vais essayer ça.
    j'avais oublié ce cas. Donc le mieux au niveau ratio efforts/fonctions reste quand même d'encapsuler une map et de faire un accès via find qui permet de dire si une clé est présente ou non.


    Exemple de code écrit en 5 mins. Loin d'êtte complet, mais c'est l'idé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
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
     
    #include <iostream>
    #include <map>
    #include <boost/function.hpp>
     
    template < class Key, class Fct > class SWITCH
    {
            public:
                void Process(const Key& k) const
                {
                        typename std::map<Key,Fct>::const_iterator it=m_map.find(k); 
                        if(it!=m_map.end())
                             it->second(k); //on pourrait rajouter un else pour traiter le default
                }
     
        void Add(const Key& k,Fct t) //aucune gestion des doublons au niveau des clés, à toi de voir et d'adapter selon tes besoins
                {
                     m_map[k]=t;
                }
     
            private:
        std::map<Key,Fct> m_map;
    };
     
    void bar(int){std::cout<<"bar"<<std::endl;}
    void foo(int){std::cout<<"foo"<<std::endl;}
    int main(int argc, char const *argv[])
    {
        SWITCH<int,boost::function<void (int)> > s;
        s.Add(0,foo);
        s.Add(1,bar);
     
        s.Process(0);
        s.Process(2);
     
        return 0;
    }
    Le problème avec cette approche, c'est pour le passage des arguments à la fonction appelée. Soit c'est direcment le clé (toujours) comme je le fais ici, soit faut rajouter des paramètre(s) template(s) à la fonction Process pour représenter les arguments et jouer avec boost:: pp (ou template variadics pour les utilisateurs de C++0x, quoi que je ne sais pas si on peut les utiliser avec une fonction)
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  12. #12
    Membre à l'essai
    Profil pro
    E/C
    Inscrit en
    Février 2006
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : E/C

    Informations forums :
    Inscription : Février 2006
    Messages : 31
    Points : 22
    Points
    22
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Dans le cas que tu présentes (une saisie clavier <-> une action), l'approche est peut être de d'abord interpréter ta saisie clavier en terme d'évènement possible de ton soft et de l'injecter dans le switch :
    1/ saisie clavier -> une_saisie
    2/ conversion une_saisie -> enum (par expl)
    3/ switch(enum) ou map<enum, DP Commande (std/boost::function trivialement)>
    Y'a un truc qui m'échappe dans ta proposition, c'est le point 2. Si l'entrée est un objet complexe (genre objet contenant une combinaison de plusieurs touches, pour rester sur cet exemple), comment tu fait pour y associer une enum ? Si ce n'est justement... par une sorte de switch sur des variables

  13. #13
    Membre à l'essai
    Profil pro
    E/C
    Inscrit en
    Février 2006
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : E/C

    Informations forums :
    Inscription : Février 2006
    Messages : 31
    Points : 22
    Points
    22
    Par défaut
    Citation Envoyé par Davidbrcz Voir le message
    [...]d'encapsuler une map et de faire un accès via find qui permet de dire si une clé est présente ou non.
    Ah oui, pas mal, je ne savais pas qu'on pouvais faire un find sur une map, du coup ça évite la boucle. Et je galerais sur l'utilisation du boost::function. Je teste tout ça. Et comme tu dis, reste le problème des arguments, je vais investiger tout ça.

Discussions similaires

  1. alterner les couleurs dans un tableau avec xsl
    Par Eithelgul dans le forum XSL/XSLT/XPATH
    Réponses: 14
    Dernier message: 03/05/2015, 23h29
  2. Réseaux : switch, routeur et wi-fi
    Par SteelBox dans le forum Hardware
    Réponses: 4
    Dernier message: 07/12/2003, 20h25
  3. Switch 1000Mbit
    Par Civodul4 dans le forum Hardware
    Réponses: 8
    Dernier message: 02/12/2003, 13h16
  4. switch
    Par drKzs dans le forum C
    Réponses: 3
    Dernier message: 07/10/2003, 07h59
  5. Réponses: 6
    Dernier message: 26/01/2003, 13h45

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