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 :

Conception policy class


Sujet :

Langage C++

  1. #1
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut Conception policy class
    Bonjour à tous,

    J'aimerais savoir si j'utilise correctement les policy class ou s'il y a un autre pattern qui correspondrait plus à mon cas.

    J'ai isolé plusieurs caractéristiques d'un algorithme dans des policy class, ainsi l'algorithme se comporte différemment suivant les policy class passées en paramètre template.

    Le problème c'est que pour quelques policy class, son comportement est de soit, ne rien faire ou faire quelque chose. Je me demande alors si c'est toujours la bonne stratégie de passer par ce pattern.

    Merci et bonne journée

  2. #2
    Membre régulier
    Inscrit en
    Décembre 2003
    Messages
    87
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 87
    Points : 87
    Points
    87
    Par défaut
    Je ne pense pas qu'il soit nécessaire dans ton cas d'utiliser le pattern policy, tu peux plutôt utiliser le pattern strategy pour configurer le comportement de ton algorithme. Le pattern policy est utile seulement dans le cas ou tu dois ajouter des méthodes à ta classe en fonction de la policy (par l'intermédiaire de l'héritage).

    A part ça les deux patterns sont très proches et c'est une bonne idée de les utiliser pour paramétrer un algorithme. Par contre je pense que si ton algo appel souvent des méthodes qui ne font rien c'est surement que la repartition du travail entre algo et policy/strategy est mal pensée. Peux tu revoir ton algo pour que les appels des méthodes qui ne font rien se passent au niveau de la strategy/policy plutôt que dans l'algo principal?

  3. #3
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    En supposant qu'on soit d'accord sur les termes, c'est à dire qu'on parle bien du pattern strategy défini par le GoF dans "Design Patterns : elements of reusable software" et du pattern policy montré dans le "Modern C++ Design" d'Andrei Alexandrescu.

    Citation Envoyé par piemur2000 Voir le message
    Je ne pense pas qu'il soit nécessaire dans ton cas d'utiliser le pattern policy, tu peux plutôt utiliser le pattern strategy pour configurer le comportement de ton algorithme. Le pattern policy est utile seulement dans le cas ou tu dois ajouter des méthodes à ta classe en fonction de la policy (par l'intermédiaire de l'héritage).
    L'intérêt des classes de policy est justement de ne pas passer par l'héritage, mais de composer l'agorithme, qui sera alors défini complètement lors de la compilation et du lien, sans avoir besoin de traitement supplémentaires au runtime. Cf. le classique singleton d'Alexandrescu dans Modern C++ Design.

    Citation Envoyé par piemur2000 Voir le message
    A part ça les deux patterns sont très proches et c'est une bonne idée de les utiliser pour paramétrer un algorithme.
    Tou a fait d'accord avec ça (modulo le fait que le pattern strategy a un impact sur l'architecture car il crée des liens entre les classes d'algorithmes, tandis que le pattern policy n'en crée pas, ce qui a aussi des avantages importants) mais...

    Citation Envoyé par piemur2000 Voir le message
    Par contre je pense que si ton algo appel souvent des méthodes qui ne font rien c'est surement que la répartition du travail entre algo et policy/strategy est mal pensée.
    ... là, je suis moins d'accord ; l'avantage du pattern policy par rapport au pattern strategy dans ce cas est que l'algorithme est optimisé par le compilateur, qui élimine le code mort ; le pattern strategy va au contraire forcer l'appel des méthodes virtuelles permettant d'affiner le comportement de l'algorithme, ce qui va entraîner une perte de temps (qui peut être négligeable dans certains cas, ou au contraire prohibitive).

    Je peux ainsi écrire :

    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
     
    template <class _Ret, class _Arg1, class _Policy>
    class algo
    {
    private:
      void do_something_on_arg1(_Arg1& arg1)
      {  _Policy().exec(arg1); }
    public:
      _Ret operator()(_Arg1 arg1)
      {
        do_something_on_arg1(arg1);
        return blablablah(arg1);
      }
    };
     
    template <class _Ret, class _Arg1>
    struct nullop
    {
       void exec(_Arg1& arg1) { }
    }
    Un appel à algo<X,Y,nullop<X,Y> >.operator() va être optimisé par le compilateur sans que j'ai besoin de faire quoi que ce soit : l'appel de exec() sera supprimé. En passant par le pattern strategy, on va appeler une méthode virtuelle, ce qui suppose deux opérations supplémentaires :

    1) initialisation du pointeur de l'objet.
    2) appel de la méthode.

    Il n'y a guère de moyen d'échaper à ce surcoût.

    En dehors de cet état de fait, il reste tout à fait possible que le pattern policy ne soit pas le bon dans ce cas précis. Si un algorithme est entièrement défini par une policy particulière, ou si la policy en question stocke un état, il sera plus judicieux d'utiliser le pattern strategy.

    Citation Envoyé par piemur2000 Voir le message
    Peux tu revoir ton algo pour que les appels des méthodes qui ne font rien se passent au niveau de la strategy/policy plutôt que dans l'algo principal?
    En cas d'application du pattern strategy, c'est une chose à faire. Dans le cas du pattern policy, ça se dispute.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  4. #4
    Membre régulier
    Inscrit en
    Décembre 2003
    Messages
    87
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 87
    Points : 87
    Points
    87
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    L'intérêt des classes de policy est justement de ne pas passer par l'héritage...
    Je ne suis pas tout à fait d'accord. J'ai en effet trop simplifié en limitant l’intérêt du pattern Policy à l'utilisation de l'héritage. Je pense cependant que l'utilisation de Policy dans la forme suivante peut être intéressant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    template <class _Ret, class _Arg1, class _Policy>
    class algo : public _Policy
    {
    private:
      void do_something_on_arg1(_Arg1& arg1)
      {  _Policy::exec(arg1); }
    public:
      _Ret operator()(_Arg1 arg1)
      {
        do_something_on_arg1(arg1);
        return blablablah(arg1);
      }
    };
    Cela permet dans des cas particuliers d'appeler des méthodes spécifiques à la Policy sur la classe Algo.

    Citation Envoyé par Emmanuel Deloget Voir le message
    l'agorithme, qui sera alors défini complètement lors de la compilation et du lien, sans avoir besoin de traitement supplémentaires au runtime...
    Encore une fois je suis d'accord sur ce point et tout ceux qui suivent . L'un des avantage du pattern policy sur le pattern strategy est l'optimisation apporté par le fait qu'on ne passe pas par des méthodes virtuelles. Ceci ne relève cependant pas du design mais seulement de l'optimisation (ce qui ne veut pas dire que ça doit être négligé).

    Citation Envoyé par Emmanuel Deloget Voir le message
    En cas d'application du pattern strategy, c'est une chose à faire. Dans le cas du pattern policy, ça se dispute.
    Loin de moi l'idée de me disputer la dessus . Je pense tout de même que l'utilisation de beaucoup de fonction vide peut révéler une mauvaise répartition de l'algorithme entre les policies/strategies et l'algo de base. Les méthodes en fonction peuvent peut-être être appelées depuis les policies/strategies. Mais bien sur c'est juste une supposition et ça dépends grandement du contexte.

    Un autre avantage des stratégies qui doit être souligné est le fait de pouvoir changer de stratégie en "live". Cela peut-être très utile si une partie de l'algo est déterminée par des choix de l'utilisateur.

  5. #5
    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
    Bonjour,

    Citation Envoyé par piemur2000 Voir le message
    Ceci ne relève cependant pas du design mais seulement de l'optimisation (ce qui ne veut pas dire que ça doit être négligé).
    Je ne partage pas cet avis. Le pattern stratégie impose de par l'utilisation de l'héritage un couplage plus fort entre le client, la stratégie abstraite et la stratégie concrète.

    Avec les politiques, le client n'impose qu'une signature à la politique. Et c'est tout. Pas d'impact sur la structure des types. Le couplage est très relaché (moins fort, au moins équivalent, ce serait l'OAP peut être).

    Donc, c'est aussi (surtout) un choix de design.

    Citation Envoyé par piemur2000 Voir le message
    Je pense tout de même que l'utilisation de beaucoup de fonction vide peut révéler une mauvaise répartition de l'algorithme entre les policies/strategies et l'algo de base.
    Avec les politiques, c'est un moyen pratique d'insérer beaucoup de point de variation à coût nul. J'ai compris la remarque d'Emmanuel en ce sens.

    Citation Envoyé par piemur2000 Voir le message
    Un autre avantage des stratégies qui doit être souligné est le fait de pouvoir changer de stratégie en "live". Cela peut-être très utile si une partie de l'algo est déterminée par des choix de l'utilisateur.
    Je pense que la force de la stratégie est bien ici : arbitrer l'algo à l'exécution.

  6. #6
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Ce débat est très intéressant, néanmoins sans vouloir vous relancez dans votre lutte de différenciation entre le policy et strategy pattern, j'aimerais vous proposez un cas plus concret.

    La situation est simple, il s'agit de réaliser un algorithme qui agit différemment si une taille est précisée ou non. Exemple :

    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
     
    template <typename size, typename error_policy, typename seq_iter>
    int somme(seq_iter begin, seq_iter end)
    {
      int sum = 0, i ;
      for( i=0; begin != end && size::reach_the_end(i) ; ++i, ++begin)
        sum += *begin ;
     
      if( i == 0)
        error_policy::empty_sequence() ;
     
      if( ! size::is_correct(i) || begin != end)
        error_policy::error_on_length() ;
     
      return sum ;
    }
    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
     
    template <size_t size_expected>
    struct with_expected_size
    {
      static bool reach_the_end ( unsigned int current_pos )
      {
         return size_expected != current_pos ;
      }
     
      static bool is_correct( unsigned int number_of_digits_hit )
      {
        return number_of_digits_hit == size_expected ;
      }
    };
     
    struct no_expected_size
    {
      static bool reach_the_end ( unsigned int current_pos )
      {
         return false ;
      }
     
      static bool is_correct( unsigned int number_of_digits_hit )
      {
        return true ;
      }
    };
    Premièrement pour différencier un nombre sans et avec taille attendue j'ai utilisé deux structures différentes. Dans ce cas, est-ce l'idéal ? J'aurais également pu faire une spécialisation true/false mais ce n'est peut-être pas aussi propre. Qu'en pensez-vous ?

    Ensuite, trouvez-vous judicieux l'utilisation de "error_policy" ? Auriez-vous utilisé une autre technique ?

    Merci beaucoup de vos réponses.

  7. #7
    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
    Citation Envoyé par Trademark Voir le message
    Qu'en pensez-vous ?
    En lisant le code, je comprends qu'il ne s'agit pas d'une fonction avec 2 statégies/politiques différentes mais d'une fonction qui pourrait avoir deux contrats différents :
    On peut alors l'écrire dans ce sens :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <numeric>
     
    template <typename contract_type, typename seq_iter>
    int somme(seq_iter begin, seq_iter end, contract_type c = contract_type())
    {
        c.check_precondition(begin,end);
     
        return std::accumulate(begin,end,0);
    }
    Le deux contrats que je comprends sont :
    => un premier assez souple : std::distance(begin,end)>=0 et

    => un second plus contraint : std::distance(begin,end)==TAILLE_DONNEE.

    Écrivons les en séparant la gestion de la rupture du contrat de sa définition :
    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
    #include <iterator>
    template<class failure_policy_t_>
    struct simple_contract
    {
        template<class it_>
        void check_precondition(it_ begin, it_ end)
        {
            if(std::distance(begin,end)<0)
            {
                failure.precondition_failed();
            }
        }
     
        explicit simple_contract(failure_policy_t_ failure_)
            :failure(failure_)
        {}
     
        failure_policy_t_ failure;
    };
    template<class failure_policy_t_>
    simple_contract<failure_policy_t_> make_simple_contrat(failure_policy_t_ failure_)
    {
        return simple_contract<failure_policy_t_>(failure_);
    }
    et
    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
    template<typename failure_policy_t_>
    struct strict_contrat
    {
        template<class it_>
        void check_precondition(it_ begin, it_ end)
        {
            if(std::distance(begin,end)!=size_expected)
            {
                failure.precondition_failed();
            }
        }
        explicit strict_contrat(int size_expected_,failure_policy_t_ failure_ = failure_policy_t_())
            :size_expected(size_expected_)
            ,failure(failure_)
        {}
     
        const int size_expected;
        failure_policy_t_ failure;
    };
     
    template<typename failure_policy_t_>
    strict_contrat<failure_policy_t_> make_strict_contrat(const int size_expected_,failure_policy_t_ failure_)
    {
        return strict_contrat<failure_policy_t_>(size_expected_,failure_);
    }
    Ensuite tu peux te construire différentes politiques pour gérer cette rupture :
    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
    #include <iostream>
     
    struct trace_policy
    {
        void precondition_failed()
        {
            std::cout<<"TRACE : precondition failed\n";
        }
    };
     
    template<typename exception_t_>
    struct exception_policy
    {
        void precondition_failed()
        {
            throw exception_t_("precondition failed");
        }
    };
    reste plus qu'à assembler :
    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
    #include <vector>
    #include <stdexcept>
     
    int main()
    {
        std::vector<int> v(10);
        somme(v.begin(),v.end(),make_simple_contrat(trace_policy()));
        somme(v.begin(),v.end(),make_strict_contrat(3,trace_policy()));
        try
        {
            somme(v.begin(),v.end(),make_strict_contrat(15,exception_policy<std::invalid_argument>()));
        }
        catch(std::exception const&e_)
        {
            std::cout<<"EXCEPTION : "<<e_.what()<<"\n";
        }
     
        try
        {
            somme(v.end(),v.begin(),make_simple_contrat(exception_policy<std::invalid_argument>()));
        }
        catch(std::exception const&e_)
        {
            std::cout<<"EXCEPTION : "<<e_.what()<<"\n";
        }
     
        return 0;
    }
    On peut enrichir pour rajouter la vérification de post-condition
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <numeric>
     
    template <typename contract_type, typename seq_iter>
    int somme(seq_iter begin, seq_iter end, contract_type c = contract_type())
    {
        c.check_precondition(begin,end);
     
        int retour = std::accumulate(begin,end,0);
        c.check_postcondition(retour);
        return retour;
    }
    etc...

    J'allais oublier, tu voulais aussi ne rien vérifier : contrat sans vérification :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    struct no_check_contrat
    {
        template<class it_>
        void check_precondition(it_ , it_ )
        {
        }
     
    };
    et hop :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    somme(v.begin(),v.end(),no_check_contrat());

  8. #8
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Merci pour votre réponse, elle m'a été utile.

    Néanmoins j'ai utilisé une solution dérivée de la votre :

    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 <size_t expected_size, class exception_size_failure = std::invalid_argument>
    struct strict_size_contract
    {
      static void respect_size_contract(size_t valid_values_counted)
      {
        BOOST_STATIC_ASSERT_MSG( expected_size > 0 , "The expected size must be greater than 0" );
        if( valid_values_counted != expected_size )
          throw exception_size_failure("Too few or too much valid values in the sequence.") ;
      }
    };
     
    template <class exception_size_failure = std::invalid_argument>
    struct no_null_size_contract
    {
      static void respect_size_contract(size_t valid_values_counted)
      {
        if( valid_values_counted == 0 )
          throw exception_size_failure("No valid value in this sequence.") ;
      }
    };
    Voyez-vous un problème suite à cette utilisation ?

    D'une autre part, je ne peux pas utiliser de std::distance, vous n'étiez pas sensé le savoir mais on ne connait la taille réelle de la séquence que après l'avoir parcourue car il peut y avoir des caractères "parasites".

    Bonne soirée.

  9. #9
    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
    Salut,

    Citation Envoyé par Trademark Voir le message
    Voyez-vous un problème suite à cette utilisation ?
    A priori, non, mais il faudrait voir comment tu as écrit la fonction somme.

    Les deux idées qui m'ont guidées pour ma réponse sont :
    => sortir la quincaillerie du test de la fonction somme vers la politique de vérification car il me semble que c'était là le point de variation et que ça facilitait la lecture de la fonction. Probablement idem pour un redressement éventuel de l'intervalle (je pense à begin != end && size::reach_the_end(i)).

    => la gestion de l'erreur relève de la politique de vérification et pas de la fonction somme

  10. #10
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Sans trop rentrer dans le détail, il ne s'agit pas uniquement d'une somme mais d'un algorithme générique utilisé pour effectuer le calcul ou la vérification d'un checkdigit, comme l'algorithme de Luhn ou Verhoeff.

    Je justifie l'utilisation d'un algorithme générique pour ne pas avoir à répéter du code.

    Ma fonction "somme" ressemble à ça :

    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 <typename algorithm, typename size_contract, typename iterator>
    int compute_checksum(const iterator &seq_begin, const iterator &seq_end )
    {
      iterator seq_begin_cpy = iterator(seq_begin) ;
      unsigned int valid_value_counter = 0;
      int checksum = 0 ;
      while( seq_begin_cpy != seq_end )
      {
        try{
          int current_valid_value = algorithm::traduce_to_valid_value( *seq_begin_cpy, valid_value_counter );
          algorithm::operate_on_valid_value( current_valid_value, valid_value_counter, checksum ) ;
          ++valid_value_counter ;
        }catch( boost::checks::traduction_exception ){
        }
        ++seq_begin_cpy ;
      }
      size_contract::respect_size_contract( valid_value_counter );
      return checksum ;
    }
    Je pourrais faire un "redressement de l'intervalle" mais il faudrait alors que "reach_the_end(i)" renvoie true si i == size_expected + 1, comme ça on sait qu'on a dépassé la limite. Je la renommerais alors "exceed_the_end(i)" ou "is_one_past_the_end(i)". J'avais évité pour des questions de lisibilité, à priori la rapidité pourrais en être affecté alors il semble alors plus judicieux d'effectuer cette vérification ?

    Avez-vous des suggestions, commentaires ?

  11. #11
    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
    Salut,
    Pourquoi passer les itérateurs par référence ? Normalement, c'est plutôt fait pour être passé par valeur. template <typename algorithm, typename size_contract, typename iterator> int compute_checksum(iterator seq_begin, iterator seq_end ).

    while => for: c'est ce à quoi ça correspond mieux : for(;seq_begin!=seq_end;++seq_begin).

    Le try/catch dans la boucle for me déroute.

    De quelle bibliothèque boost vient boost::checks::traduction_exception

  12. #12
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Merci de cette réponse, j'ai corrigé les détails. Boost::checks est la bibliothèque boost dont je suis chargé dans le cadre du Google Summer Of Code.

    L'idée du try/catch est de ne pas traiter les caractères invalides (par exemple un ISBN pourra être de la forme : 978-201280550-7). Je trouve que, dans ce cas, l'exception est plus intéressante qu'un code de retour. Pensez-vous qu'il aurait mieux valu un code comme celui-là :

    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
     
    template<typename algo, typename iter_seq>
    static int traduce_the_next_valid_value( iter_seq b, iter_seq e, int valid_value_counter)
    {
      while(b != e)
      {
         try{ ... break; }
         catch( boost::checks::traduction_exception ) { 
            ++b;
         }
      }
    }
     
    ***
    ...
    int current_valid_value = traduce_the_next_valid_value<algorithm>( seq_begin, seq_end, valid_value_counter );
    for(; seq_begin != seq_end; ++seq_begin )
    {
          algorithm::operate_on_valid_value( current_valid_value, valid_value_counter, checksum ) ;
          ++valid_value_counter ;
          current_valid_value = traduce_the_next_valid_value<algorithm>( seq_begin, seq_end, valid_value_counter );
      }
    ....
    Je ne trouvais pas ça particulièrement plus lisible à cause de l'instruction en dehors de la boucle (ou du if potentiel à rajouter à l'intérieure). En plus ca fait un peu redondant les deux boucles.
    Avez-vous une autre idée en tête ?

  13. #13
    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
    Citation Envoyé par Trademark Voir le message
    Avez-vous une autre idée en tête ?
    En enveloppant ton itérateur d'un Boost.FilterIterator
    Ca permettrait aussi de gérer le traduce_next_valid_value :
    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
    template <typename algorithm, typename size_contract, typename iterator>
    int compute_checksum( iterator seq_begin, iterator seq_end )
    {
       typedef boost::filter_iterator<typename algorithm::filter,iterator> type_iterator;
       type_iterator filter_begin(seq_begin,seq_end) ;
       type_iterator filter_end(seq_end,seq_end) ;
       unsigned int valid_value_counter = 0;
       int checksum = 0 ;
       for(; filter_begin != filter_end; ++filter_begin )
       {
          algorithm::operate_on_valid_value( *filter_begin, valid_value_counter, checksum ) ;
          ++valid_value_counter ;
       }
       size_contract::respect_size_contract( valid_value_counter );
       return checksum ;
    }

  14. #14
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Merci pour cette solution que je connaissais pas. Elle peut sembler très intéressante mais traduce_next_valid_value doit renvoyer un entier "traduit" de la valeur initiale, par exemple si *seq_begin == 'X' ou 'x' alors traduce_next_valid_value renvoie 10.

    Le prédicat ressemblerait par exemple à ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    template <typename value>
    static bool traduce_to_valid_value(const value &current_value, const unsigned int valid_value_counter )
      {
        try{
          boost::lexical_cast<int>( current_value ) ;
        }catch( boost::bad_lexical_cast ){
          return false ;
        }
        return true ;
      }
    Pas de problème me direz-vous, mais dans la boucle je suis obligé de recommencer le traitement pour obtenir la bonne valeur avec cette fonction, et il y donc répétition :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
      template <typename value>
      static int traduce_to_valid_value(const value &current_value, const unsigned int valid_value_counter )
      {
        return boost::lexical_cast<int>( current_value ) ;
      }
    On revient assez vite au code initial, qui est (pour rappel) :

    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
    template <typename algorithm, typename size_contract, typename iterator>
    int compute_checksum(iterator seq_begin, iterator seq_end )
    {
      unsigned int valid_value_counter = 0;
      int checksum = 0 ;
      for(; seq_begin != seq_end; ++seq_begin )
      {
        try{
          int current_valid_value = algorithm::traduce_to_valid_value( *seq_begin, valid_value_counter );
          algorithm::operate_on_valid_value( current_valid_value, valid_value_counter, checksum ) ;
          ++valid_value_counter ;
        }catch( boost::checks::traduction_exception ){
        }
      }
      size_contract::respect_size_contract( valid_value_counter );
      return checksum ;
    }
    Donc je ne sais toujours pas comment me passer du try/catch.

  15. #15
    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
    Effectivement. On pourrait faire une composition entre un Boost.FilterIterator et un Boost.TransformIterator mais ça ferait probablement refaire 2x la conversion lexical_cast.

    Ceci dit, tu peux quand même rester sur Boost.Iterator pour faire ton propre FilterAndTransformIterator à partir de Boost.Iterator.Adaptator

    Je me demande s'il ne faut pas lever le stylo (enfin, le doigt du clavier) et revoir l'algorithme : son objectif, ses préconditions, ses post conditions, ses points de variations : politique algorithme, filtrage des données, transformation des données, etc... J'ai un peu le sentiment d'avancer à taton et c'est assez frustrant

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

Discussions similaires

  1. Conception de classes
    Par Seth77 dans le forum Général Dotnet
    Réponses: 8
    Dernier message: 15/01/2007, 15h57
  2. [POO] conception des classes
    Par poukill dans le forum C++
    Réponses: 229
    Dernier message: 19/07/2006, 08h28
  3. [conception] reprise classes mal pensées
    Par in dans le forum Général Java
    Réponses: 8
    Dernier message: 05/06/2006, 13h45
  4. Conception de classe
    Par Azharis dans le forum C++
    Réponses: 9
    Dernier message: 17/12/2005, 10h15
  5. probleme de conception de classe
    Par NhyMbuS dans le forum C++
    Réponses: 2
    Dernier message: 08/05/2005, 17h10

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