Partage
  • Partager sur Facebook
  • Partager sur Twitter

Classe Fraction : le cas de la division par 0

Comment la gerer....?

Sujet résolu
    23 octobre 2018 à 11:10:22

    LilyKianii a écrit:

    C'était trop imagé ? Grossièrement, tout ce que tu peux pas handle, tu assert.

    Bah ça risque surtout de poser problème en release, où l'idée de la plupart des builds c'est de virer les assert. Donc dans ce cas là ça va juste vider ton bloc catch et silencieusement commencer à faire n'importe quoi. Tu aurais dit abort, d'accord, mais assert c'est casse gueule.

    Mais sinon assert pour tout ce qui est respect de contrat, c'est très bien :) .

    romantik a écrit:

    C'est toute la philosophie de la PpC, et elle me convainc vraiment ! Je trouve les billets de lmghs vraiment intéressant.

    C'est plus drôle quand on en fait de la preuve derrière.

    -
    Edité par Ksass`Peuk 23 octobre 2018 à 11:11:28

    • Partager sur Facebook
    • Partager sur Twitter

    Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

      23 octobre 2018 à 13:22:06

      Ksass`Peuk a écrit:

      Bah ça risque surtout de poser problème en release, où l'idée de la plupart des builds c'est de virer les assert. Donc dans ce cas là ça va juste vider ton bloc catch et silencieusement commencer à faire n'importe quoi. Tu aurais dit abort, d'accord, mais assert c'est casse gueule.

      Mais sinon assert pour tout ce qui est respect de contrat, c'est très bien :) .

      C'est pas justement parce qu'elles sont enlevées en release que c'est intéressant ?

      Pour moi, ça permet d'identifier un problème non résoluble au moment T du programme (et à une date pré-release "client" R-1) afin de réagir aux moments T-1.

      Me trompé-je ?

      -
      Edité par LilyKianii 23 octobre 2018 à 13:30:02

      • Partager sur Facebook
      • Partager sur Twitter
        23 octobre 2018 à 13:31:09

        LilyKianii a écrit:

        C'est pas justement parce qu'elles sont enlevées en release que c'est intéressant ?

        Si mais justement, je ne vois toujours pas le rapport avec une exception. Ou en tout cas, ça ne semble être qu'un sous cas de l'usage des assertions. A savoir qu'effectivement, avoir une exception levée que personne ne sait gérer en prod c'est un problème, et ça ne devrait pas arriver. Mais si l'exception ne peut plus arriver, à quoi bon avoir un bloc catch (vide) ?

        A côté de ça, l'assertion est plus générale que ça sur le plan des type d'erreurs gérées : on ne s'occupe pas que des exceptions qui pourraient survenir mais également des contrats non-respectés pour une raison X ou Y. Et plus spécifique sur le plan des sources d'erreur gérées : les conneries du développeur. Et les conneries du développeurs, comme normalement en release, il n'est plus sensé y en avoir, c'est dommage de se traîner les tests qui correspondent à ces conneries, d'où le fait d'utiliser une assertion pour que ce test dégage complètement.

        • Partager sur Facebook
        • Partager sur Twitter

        Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

          23 octobre 2018 à 13:40:23

          #ifdef DEBUG
          if (denominateur == 0) {
             throw ...
          }
          #endif
          

          -
          Edité par michelbillaud 23 octobre 2018 à 13:40:47

          • Partager sur Facebook
          • Partager sur Twitter
            23 octobre 2018 à 13:44:23

            C'est vrai que c'était pas assez lourd d'écrire du C++.

            (Mais on pourrait peut être mettre ça dans une macro qu'on appellerai assert).

            -
            Edité par Ksass`Peuk 23 octobre 2018 à 13:45:50

            • Partager sur Facebook
            • Partager sur Twitter

            Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

              23 octobre 2018 à 14:06:56

              Ksass`Peuk a écrit:

              Si mais justement, je ne vois toujours pas le rapport avec une exception.

              Aucun

              Ksass`Peuk a écrit:

              Mais si l'exception ne peut plus arriver, à quoi bon avoir un bloc catch (vide) ?

              Ah ! :euh:  Méprise ! Je me suis encore mal exprimée ! Il n'y a jamais eu d'histoire de catch (vide ou non) dans le cas que je soulevais.

               Cela me permet juste de savoir où je suis censée apposer mes contrats.

              -
              Edité par LilyKianii 23 octobre 2018 à 14:08:30

              • Partager sur Facebook
              • Partager sur Twitter
                23 octobre 2018 à 16:55:46

                Moi qui pensais avoir pose une simple question anodine, je me trompais lourdement semblerait il... YOSH, du coup j'ai essayer avec un assert et le compilo n'as pas trop aime l’exécution...

                /* header */
                
                class Fraction
                {
                    public:
                        Fraction(int x = 0, int y = 1);
                        void show() const;
                        void simplify();
                        int getNumerator() const;
                        int getDenominator() const;
                    private:
                        int mNum{0};
                        int mDen{1};
                };
                
                /* source */
                
                Fraction::Fraction(int x, int y) : mNum{x}, mDen{y}
                {
                    int pgcd = 0;
                
                    assert(y == 0 && "Error: Denominator shouldn't be equal to 0.");
                    pgcd = findPGCD(mNum, mDen);
                    if (pgcd != 0) {
                        mNum /= pgcd;
                        mDen /= pgcd;
                    }
                    if ((mNum < 0 && mDen < 0) || (mNum > 0 && mDen < 0)) {
                        mNum *= -1;
                        mDen *= -1;
                    }
                }

                Erreurs d’exécution

                Donc je suis un peu perdu, je voudrai éviter d'utiliser les voix facile type exit() qui ne sont pas très "propre" je trouve mais plus je lis, moins je comprend ce qu'il vaut mieux faire...

                Il faut savoir que je commence a peine le c++ et que je ne suis pas un pro de la programmation donc je ne comprend pas tout les mots clé ni même certain de vos exemple o_O

                -
                Edité par Rymfire 23 octobre 2018 à 17:01:58

                • Partager sur Facebook
                • Partager sur Twitter
                Give a man a fish and you feed him for a day; teach a man to fish and you feed him for a lifetime.
                  23 octobre 2018 à 17:01:15

                  Il faut juste que tu mettes le bon signe. Ce que tu veux assurer, c'est que "y" est différent de 0, pas égal. D'ailleurs, il devrait simplement être strictement positif.

                  Et vire ces getters, ils ne servent à rien. Tu devrais rendre simplify privée aussi (et garantir que c'est toujours simplifié). Et virer cette fonction show au profit d'une surcharge de <<. Et comment PGCD pourrait te renvoyer 0 ?

                  -
                  Edité par Ksass`Peuk 23 octobre 2018 à 17:04:26

                  • Partager sur Facebook
                  • Partager sur Twitter

                  Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

                    23 octobre 2018 à 17:04:47

                    Ksass`Peuk a écrit:

                    Il faut juste que tu mettes le bon signe. Ce que tu veux assurer, c'est que "y" est différent de 0, pas égal.

                    Et vire ces getters, ils ne servent à rien.

                    -
                    Edité par Ksass`Peuk il y a moins de 30s


                    Fraction::Fraction(int x, int y) : mNum{x}, mDen{y}
                    {
                        int pgcd = 0;
                    
                        assert(y != 0);
                        pgcd = findPGCD(mNum, mDen);
                        if (pgcd != 0) {
                            mNum /= pgcd;
                            mDen /= pgcd;
                        }
                        if ((mNum < 0 && mDen < 0) || (mNum > 0 && mDen < 0)) {
                            mNum *= -1;
                            mDen *= -1;
                        }
                    }

                    On es bien d'accord sur le code? parce que ça crash comme même... :o
                    • Partager sur Facebook
                    • Partager sur Twitter
                    Give a man a fish and you feed him for a day; teach a man to fish and you feed him for a lifetime.
                      23 octobre 2018 à 17:06:34

                      Rymfire a écrit:

                      On es bien d'accord sur le code? parce que ça crash comme même... :o

                      quand même.

                      Mais sans les entrées du programme et l'appelant, on ne peut pas dire grand chose. Et ne déclare pas tes variables en avance.

                      -
                      Edité par Ksass`Peuk 23 octobre 2018 à 17:07:14

                      • Partager sur Facebook
                      • Partager sur Twitter

                      Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

                        23 octobre 2018 à 17:19:18

                        Ksass`Peuk a écrit:

                        Il faut juste que tu mettes le bon signe. Ce que tu veux assurer, c'est que "y" est différent de 0, pas égal. D'ailleurs, il devrait simplement être strictement positif.

                        Et vire ces getters, ils ne servent à rien. Tu devrais rendre simplify privée aussi (et garantir que c'est toujours simplifié). Et virer cette fonction show au profit d'une surcharge de <<. Et comment PGCD pourrait te renvoyer 0 ?

                        -
                        Edité par Ksass`Peuk il y a 4 minutes


                        J'avais mal compris la fonction de assert()...

                        Pourquoi mon y devrait il être strictement positif? Mon constructeur gère ce cas en faisant remonter ou en supprimant le signe moins si besoin.

                        En ce qui concerne simplify(), la fonction ne devrais pas avoir a être utilise puisque j'ai fais en sort que la fraction soit toujours simplifie mais je l'ai mis au cas ou...

                        Qu'es ce que tu appelle surcharge de redirection?

                        PGCD = 0, c'est une erreur, c'est pas possible sinon on en reviens au même problème. Je l’enlève de suite.

                        Ksass`Peuk a écrit:

                        Rymfire a écrit:

                        On es bien d'accord sur le code? parce que ça crash comme même... :o

                        quand même.

                        Mais sans les entrées du programme et l'appelant, on ne peut pas dire grand chose. Et ne déclare pas tes variables en avance.

                        -
                        Edité par Ksass`Peuk il y a 9 minutes


                        #include <iostream>
                        #include "Fraction.h"
                        
                        const int END = 0;
                        
                        int main()
                        {
                            Fraction f1;
                            Fraction f2{2};
                            Fraction f3{2, 0};
                            Fraction f4{f2 * f3};
                        
                            f4.show();
                            return (END);
                        }
                        


                        Qu'es ce que tu appelle déclarer mes variables en avance? C'est ça?

                            private:
                                int mNum{0};
                                int mDen{1};

                        J'arrive pas a me débarrasser de ce comme même xD

                        -
                        Edité par Rymfire 23 octobre 2018 à 17:26:16

                        • Partager sur Facebook
                        • Partager sur Twitter
                        Give a man a fish and you feed him for a day; teach a man to fish and you feed him for a lifetime.
                          23 octobre 2018 à 17:29:37

                          Rymfire a écrit:

                          Pourquoi mon y devrait il être strictement positif? Mon constructeur gère ce cas en faisant remonter ou en supprimant le signe moins si besoin.

                          Ça se défend, même si j'aurai tendance à l'interdire simplement, ça fait moins de cas à gérer. Par contre ta condition peut être simplifiée à mDen < 0.

                          Rymfire a écrit:

                          En ce qui concerne simplify(), la fonction ne devrais pas avoir a être utilise puisque j'ai fais en sort que la fraction soit toujours simplifie mais je l'ai mis au cas ou...

                          Mets toujours le minimum de fonctionnalités. Pourquoi avoir cette fonction si la propriété est toujours vraie ?

                          Rymfire a écrit:

                          Qu'es ce que tu appelle surcharge de redirection?

                          https://en.cppreference.com/w/cpp/language/operators#Stream_extraction_and_insertion

                          Rymfire a écrit:

                          Qu'es ce que tu appelle déclarer mes variables en avance? C'est ça?

                          Non, ça :

                          int pgcd = 0;
                           
                          assert(y != 0);
                          pgcd = findPGCD(mNum, mDen);

                          La première ligne ne sert à rien, déclare directement pgcd lors de l'appel à findPGCD.

                          Sinon concernant, ton code, c'est normal que tu aies un crash, et c'est exactement ce que tu veux. Ici, le développeur (toi) tente de créer une fraction avec la valeur 0 en dénominateur, c'est illégal et donc on crash le programme. Et si quelque chose comme ça arrive, on exécute avec le debugger et on pourra retrouver l'appel fautif.

                          -
                          Edité par Ksass`Peuk 23 octobre 2018 à 17:30:15

                          • Partager sur Facebook
                          • Partager sur Twitter

                          Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

                            23 octobre 2018 à 17:57:05

                            Faut faire les choses dans le bon ordre, aussi

                            1.si dénominateur = 0 => badaboum

                            2. Normaliser le signe : si le dénominateur est négatif, changer les signes

                            3. réduire : diviser numérateur et dénominateur par le pgcd  de la _valeur absolue_ du numérateur et du dénominateur (qui est déjà positif)

                            Pourquoi normaliser dans le constructeur ? Chaque fois qu'on aura à faire une opération qui construit une fraction, il y aura besoin de le faire, alors autant laisser au constructeur la tache de normaliser la représentation.

                            Ecrire la somme

                            Fraction plus(const Fraction & f) const {
                               return {
                                   numerateur * f.denominateur + f.numerateur * denominateur,
                                  denominateur * f.denominateur
                               };
                            }

                            c'est déjà assez emmerdant comme ça.



                            -
                            Edité par michelbillaud 23 octobre 2018 à 17:57:38

                            • Partager sur Facebook
                            • Partager sur Twitter
                              23 octobre 2018 à 18:01:27

                              Ksass`Peuk a écrit:

                              Rymfire a écrit:

                              En ce qui concerne simplify(), la fonction ne devrais pas avoir a être utilise puisque j'ai fais en sort que la fraction soit toujours simplifie mais je l'ai mis au cas ou...

                              Mets toujours le minimum de fonctionnalités. Pourquoi avoir cette fonction si la propriété est toujours vraie ?

                              Effectivement ça sert a rien ^^ 

                              Ksass`Peuk a écrit:

                               Rymfire a écrit:

                              Qu'es ce que tu appelle surcharge de redirection?

                              https://en.cppreference.com/w/cpp/language/operators#Stream_extraction_and_insertion

                              Je vais aller lire ça.. EDIT: Ah mais en fais je sais ce que c'est j'avais juste oublie le terme... :-°

                              Ksass`Peuk a écrit:

                              Sinon concernant, ton code, c'est normal que tu aies un crash, et c'est exactement ce que tu veux. Ici, le développeur (toi) tente de créer une fraction avec la valeur 0 en dénominateur, c'est illégal et donc on crash le programme. Et si quelque chose comme ça arrive, on exécute avec le debugger et on pourra retrouver l'appel fautif. 

                              Ah oui okay je vois. C'est ce message qui est affiche par l'assert, cependant ça n’empêche pas a mon programme de crasher.

                              -
                              Edité par Rymfire 23 octobre 2018 à 18:09:30

                              • Partager sur Facebook
                              • Partager sur Twitter
                              Give a man a fish and you feed him for a day; teach a man to fish and you feed him for a lifetime.
                                23 octobre 2018 à 18:06:56

                                Rymfire a écrit:

                                Ah oui okay je vois. C'est ce message qui est affiche par l'assert, cependant ça n’empêche pas a mon programme de crasher.

                                En effet, personne ne peut t'empêcher d'écrire des programmes qui se débrouillent pour appeler le constructeur avec denominateur = 0.



                                -
                                Edité par michelbillaud 23 octobre 2018 à 18:07:25

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  23 octobre 2018 à 18:20:01

                                  michelbillaud a écrit:

                                  Pourquoi normaliser dans le constructeur ? Chaque fois qu'on aura à faire une opération qui construit une fraction, il y aura besoin de le faire, alors autant laisser au constructeur la tache de normaliser la représentation.

                                  La je t'avoue que tu m'as perdus...

                                  michelbillaud a écrit:

                                  Ecrire la somme

                                  Fraction plus(const Fraction & f) const {
                                     return {
                                         numerateur * f.denominateur + f.numerateur * denominateur,
                                        denominateur * f.denominateur
                                     };
                                  }
                                  J'ai pas encore fais la somme, c'est la prochaine étape. J'etait en train de coder mon operator* lorsque j'ai fais par hasard (pour un test) une division par 0. Donc me voila. (Pourquoi la multiplication en premier? C'etait plus simple je trouvais ^^)

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                  Give a man a fish and you feed him for a day; teach a man to fish and you feed him for a lifetime.
                                    23 octobre 2018 à 18:51:11

                                    Rymfire a écrit:

                                    michelbillaud a écrit:

                                    Pourquoi normaliser dans le constructeur ? Chaque fois qu'on aura à faire une opération qui construit une fraction, il y aura besoin de le faire, alors autant laisser au constructeur la tache de normaliser la représentation.

                                    La je t'avoue que tu m'as perdus...

                                    Tu dois comprendre que l'idée qui se cache derrière le constructeur, c'est de nous permettre d'obtenir ... une instance directement utilisable de la classe.

                                    Donc, quand tu crées ta variable de type fraction, tu veux que tout ce qui devra être fait lors de la création de ta variable ait déjà été fait. Il n'est pas question que le type qui crée la  variable doive :

                                    • commencer normaliser les valeur
                                    • commencer à calculer le pgcd
                                    • appliquer le pgcd au numérateur et au dénominateur.

                                    Parce que tu dois te dire que ... l'utilisateur est un imbécile distrait, et que, s'il doit passer par quatre étape (les trois que je viens de citer, plus la création de la variable), tu dois t'attendre à ce qu'il en oublie une ou l'autre à un moment ou à un autre.

                                    Et, juste pour rire, dis toi bien que tu est peut-être le développeur de ta classe fraction, mais que tu "perds ce statu" une fois que tu a terminer d'écrire le code.  Tout ce que tu fera avec la classe fraction par la suite, tu le fera en tant ... qu'utilisateur :p

                                    En un mot comme en cent, tu dois respecter le conseil de Scott Meyers:

                                    Make interfaces easy to use correctly and hard to use incorrectly

                                    (Rendez vos interfaces faciles à utiliser correctement et difficile à utiliser de manière incorrecte)

                                    En évitant à l'utilisateur d'avoir à penser qu'il doit encore appliquer une procédure particulière après avoir créé sa variable, tu rend ta classe ... facile à utiliser correctement et difficile à utiliser de manière incorrecte ;)

                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                    Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                                      23 octobre 2018 à 22:51:32

                                      Je dirais que c'est la combinaison de RAII et d'une approche en fournisseur de service. Une fois construite, mon instance est directement utilisable et son utilisation est de fournir un service. J'ai de fait une utilisation extrêmement simple:

                                      • Je construis l'instance de l'objet
                                      • J'appelle le service dont j'ai besoin dans son interface, si on met du SRP là dedans, le service, il n'y en a qu'un donc difficile de se tromper ^^
                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                      Mettre à jour le MinGW Gcc sur Code::Blocks. Du code qui n'existe pas ne contient pas de bug
                                        24 octobre 2018 à 11:15:43

                                        koala01 a écrit:

                                        Rymfire a écrit:

                                        michelbillaud a écrit:

                                        Pourquoi normaliser dans le constructeur ? Chaque fois qu'on aura à faire une opération qui construit une fraction, il y aura besoin de le faire, alors autant laisser au constructeur la tache de normaliser la représentation.

                                        La je t'avoue que tu m'as perdus...

                                        Tu dois comprendre que l'idée qui se cache derrière le constructeur, c'est de nous permettre d'obtenir ... une instance directement utilisable de la classe.

                                        Donc, quand tu crées ta variable de type fraction, tu veux que tout ce qui devra être fait lors de la création de ta variable ait déjà été fait. Il n'est pas question que le type qui crée la  variable doive :

                                        • commencer normaliser les valeur
                                        • commencer à calculer le pgcd
                                        • appliquer le pgcd au numérateur et au dénominateur.

                                        Parce que tu dois te dire que ... l'utilisateur est un imbécile distrait, et que, s'il doit passer par quatre étape (les trois que je viens de citer, plus la création de la variable), tu dois t'attendre à ce qu'il en oublie une ou l'autre à un moment ou à un autre.

                                        Et, juste pour rire, dis toi bien que tu est peut-être le développeur de ta classe fraction, mais que tu "perds ce statu" une fois que tu a terminer d'écrire le code.  Tout ce que tu fera avec la classe fraction par la suite, tu le fera en tant ... qu'utilisateur :p

                                        En un mot comme en cent, tu dois respecter le conseil de Scott Meyers:

                                        Make interfaces easy to use correctly and hard to use incorrectly

                                        (Rendez vos interfaces faciles à utiliser correctement et difficile à utiliser de manière incorrecte)

                                        En évitant à l'utilisateur d'avoir à penser qu'il doit encore appliquer une procédure particulière après avoir créé sa variable, tu rend ta classe ... facile à utiliser correctement et difficile à utiliser de manière incorrecte ;)


                                        La j'ai tout compris! Merci pour votre aide tout le monde!! J'suis content j'ai appris plein de nouvelles choses :D
                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                        Give a man a fish and you feed him for a day; teach a man to fish and you feed him for a lifetime.
                                          24 octobre 2018 à 13:12:44

                                          lmghs a écrit:

                                          Ca fait beaucoup à expliquer, je passe en mode fainéant

                                          1- https://stackoverflow.com/questions/121162/what-does-the-explicit-keyword-mean

                                          2- opérateur de conversion, explicite (c++11) -- en gros, on converti dans l'autre sens.

                                          3- Prends-ton temps, tu n'y es pas encore. Le premier permet d'exiger du compilateur la garantie que des expressions seront évaluées à la compilation. Le second garantit qu'une fonction ne lance pas d'exception -- c'est plus compliqué en vrai. Comprends et vis d'abord le RAII.


                                          Dans ce message, nous parlions de noexcept et tu me disais que le second (noexcept) ne lance pas d'exception. Or pourquoi dans le cours de OC, dit-on que dans le code suivant :

                                          class exception 
                                          
                                          {
                                          
                                          public:
                                          
                                              exception() throw(){ } //Constructeur.
                                          
                                              virtual  exception() throw(); //Destructeur.
                                          
                                           
                                          
                                              virtual const char* what() const throw(); //Renvoie une chaîne "à la C" contenant des infos sur l'erreur.
                                          
                                          };

                                          la chose suivante :

                                          "Les méthodes de la classe sont suivies du mot-clé throw(). Cela sert à indiquer que ces méthodes ne vont pas lancer d'exceptions... ce qui est plutôt judicieux parce que, si la classe exception commence à lancer des exceptions, on n'est pas sorti de l'auberge.
                                          Indiquer qu'une méthode ne lance pas d'exception est un mécanisme du C++ très rarement utilisé. En fait, cette classe est à peu près le seul endroit où vous verrez cela."

                                          Quelle est dans ce cas, la différence entre throw() et noexcept ?

                                          Merci pour les explications précises

                                          -
                                          Edité par pseudo-simple 24 octobre 2018 à 13:13:33

                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            24 octobre 2018 à 13:19:00

                                            (Il faut mieux eviter de poser des questions qui ne concernent pas la discussion initiale dans la meme discussion, ca devient illisible. Poses ta question dans une nouvelle discussion, en mettant un lien vers celle ci)
                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              24 octobre 2018 à 13:32:14

                                              YES, man a écrit:


                                              Dans ce message, nous parlions de noexcept et tu me disais que le second (noexcept) ne lance pas d'exception. Or pourquoi dans le cours de OC, dit-on que dans le code suivant :

                                              Parce que, encore une fois, le cours est obsolète: avant C++11, on pouvait indiquer la liste des exceptions susceptibles d'être lancées par une fonction, avec comme effet de bord que, si tu faisais appel à une fonction qui lancait une exception absente de la liste, le programme appelait automatiquement unexpected_error()

                                              Du coup, la spécification des exceptions devait etre strictement exhaustive reprendre l'ensemble exceptions susceptibles d'être lancées par l'ensemble des fonctions  appelées de manière directe ou indirecte qui n'avaient pas été gérées (ou qui avaient été relancées), ce qui laissait une grande place à "certains oublis" susceptible de foutre tout le système en l'air.

                                              Si bien que le fait qu'une fonction proche de

                                              void foo() trhow(){
                                                  /* ... */
                                              }

                                              ne puisse lancer aucune exception n'était qu'un effet secondaire du au fait qu'une fonction proche de

                                              void bar() throw(std::bad_alloc){
                                                  /* ... */
                                              }

                                              ne pouvait pas lancer d'autre exception qu'une ... qu'une exception de / dérivée du type std::bad_alloc

                                              C++11 a changé radicalement de point de vue, en disant que nous pouvons spécifiquement indiquer qu'une fonction ne peut lancer aucune exception, ce qui permet au compilateur

                                              • de n'accepter que l'appel de fonction qui se sont également engagées à ne pas lancer d'exceptions (et cela de manière récursives) si la fonction ne peut pas lancer d'exception
                                              • d'accepter que n'importe quelle exception soit lancée -- de manière directe ou indirecte -- si la fonction peut lancer une exception

                                              Ce qui rend les choses beaucoup plus simples ;)

                                              -
                                              Edité par koala01 24 octobre 2018 à 13:37:00

                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                              Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                                                26 octobre 2018 à 9:09:01

                                                michelbillaud a écrit:

                                                Ca serait peut être mieux de lancer une exception spécifique.

                                                class ExceptionConstructionRationnel 
                                                	: public std::runtime_error
                                                {
                                                public:	
                                                	ExceptionConstructionRationnel() 
                                                		: std::runtime_error("dénominateur nul") 
                                                	{
                                                	}
                                                };
                                                
                                                class Rationnel {
                                                	int num;
                                                    int den;
                                                
                                                public:
                                                    Rationnel(int n = 0, int d = 1)
                                                     : num(n), den(d)
                                                    {
                                                        if (d == 0) {
                                                	        throw ExceptionConstructionRationnel();
                                                        }
                                                    }
                                                };

                                                Je ne savais pas qu'on était sur le forum Java.

                                                Sinon, à mort la programmation défensive. C'est pas à une fonction de vérifier la connerie du programmeur, paye tes performances parce que toutes les fonctions "bas niveau" sont défensives et non-noexcept.

                                                -
                                                Edité par markand 26 octobre 2018 à 9:09:59

                                                • Partager sur Facebook
                                                • Partager sur Twitter

                                                git is great because Linus did it, mercurial is better because he didn't.

                                                  26 octobre 2018 à 9:43:22

                                                  Oui y a qu'a supposer qu'on va pas se tromper. C'est bien connu, si on est intelligent et si on s'applique en programmant, on ne fait jamais d'erreur.

                                                  "What could possibly go wrong ?"

                                                  -
                                                  Edité par michelbillaud 26 octobre 2018 à 9:46:59

                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    26 octobre 2018 à 10:02:12

                                                    Si je comprends bien @michelbillaud, tu places une condition et une exception avant chaque opération que tu réalises dans un programme du coup ? Typiquement, si tu veux faire "a+b", tu fais un check pour vérifier l'absence de débordement avant, et si ça déborde, tu jettes une exception. Et si tu fais une division, tu vérifies avant que tu n'as pas un diviseur qui est nul, et si c'est le cas tu jettes aussi une exception, et ce pour la totalité du programme. Et j'imagine que tu monitores la mémoire au runtime aussi.

                                                    Tu peux me dire que je caricature tes propos, mais je ne serais pas d'accord. En préférant mettre une condition et une exception pour le dénominateur à 0, tu vérifies manuellement une division par 0 dans le programme.

                                                    Ou alors peut être que tu utilises le debugger et que tu fais confiance à tes tests pour t'assurer que tu n'as pas introduit de telles erreurs ?

                                                    Quand on passe un programme en production, il est censé être testé et validé. Donc de deux choses l'une :

                                                    • soit tu considères que le niveau de fiabilité que tu as validé est suffisant pour ta tâche et alors pourquoi laisser ces contrôles ?
                                                    • soit tu considères que ce n'est pas le cas, et à ce moment là, améliore la validation.

                                                    -
                                                    Edité par Ksass`Peuk 26 octobre 2018 à 10:03:28

                                                    • Partager sur Facebook
                                                    • Partager sur Twitter

                                                    Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

                                                      26 octobre 2018 à 10:25:26

                                                      Ksass`Peuk a écrit:

                                                      Si je comprends bien @michelbillaud, tu places une condition et une exception avant chaque opération que tu réalises dans un programme du coup ?

                                                      Si la conclusion que tu tires de ce que tu penses avoir compris te parait totalement absurde et déraisonnable, peut être que tu n'as pas compris, ou que tu es en train d'extrapoler de façon absurde et déraisonnable, tout en affirmant que tu ne caricatures pas ? :-)


                                                      Faut peut être revenir à la question posée, qui était de savoir comment signaler un problème détecté pendant la construction d'un objet.

                                                       Donc si je veux arrêter mon programme 
                                                      et dire a mon utilisateur que sa fraction ne vas pas... 
                                                      soit c'est exit() mais c'est pas propre soit je sais pas... 
                                                      Comment faut - il gérer ce cas?

                                                      et pas de savoir si il faut essayer ou non de détecter les problèmes "qui n'arrivent jamais" dans les constructeurs dans les programmes en production, ou si on peut avoir une confiance totale dans les tests avant mise en production.

                                                      Donc la panoplie disponible, c'est

                                                      • if(...) exit(...)
                                                      • if (...) throw ...
                                                      • assert(....)

                                                       l'avantage de throw sur exit, c'est que ça provoque (sur une exception non rattrapée) un crash plus circonstancié.

                                                      -
                                                      Edité par michelbillaud 26 octobre 2018 à 10:38:08

                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        26 octobre 2018 à 10:37:22

                                                        michelbillaud a écrit:

                                                        Si la conclusion que tu tires de ce que tu penses avoir compris parait totalement absurde et déraisonnable, peut être que tu n'as pas compris, et que tu es en train d'extrapoler ? :-)

                                                        Ou alors (autre option hein). Le fait que tu trolles en permanence n'arrive pas à expliquer pourquoi sur un programme validé convenablement, le code :

                                                        Rationnel(int n = 0, int d = 1)
                                                         : num(n), den(d)
                                                        {
                                                          if (d == 0) {
                                                            throw ExceptionConstructionRationnel();
                                                          }
                                                        }

                                                        est préférable à :

                                                        Rationnel(int n = 0, int d = 1)
                                                         : num(n), den(d)
                                                        {
                                                          assert(d != 0);
                                                        }

                                                        Alors que dans le second :

                                                        • on ne paie pas de surcoût à l'exécution quand on est en production,
                                                        • si notre utilisateur a une contrainte qui l'empêche d'avoir des exceptions, il peut quand même utiliser notre lib.

                                                        Parce pour l'instant le seul argument en la faveur du premier code que tu as avancé c'est :

                                                        michelbillaud a écrit:

                                                        Alors que les assertions désactivés par accident, ça arrive (dans les options de compilation).

                                                        Autant dire que je serai curieux de voir le nombre d'intégration continues qui tournent avec des programmes en mode release, et encore plus curieux de voir quelqu'un justifier l'absence de Debug dans une phase de validation.

                                                        Littéralement tout le reste, c'est du bashing sans argument. Donc au bout d'un moment, faudrait pas t'étonner qu'on te prend plus au sérieux.

                                                        EDIT : Après EDIT de michel. Bon bah voilà on a un second argument. Le crash plus circonstancié. Je suis pas sûr d'adhérer à l'idée : quand on a une assertion, on a normalement tout ce qu'il faut dans la stack-trace quand on repasse le test au debugger.

                                                        -
                                                        Edité par Ksass`Peuk 26 octobre 2018 à 10:39:33

                                                        • Partager sur Facebook
                                                        • Partager sur Twitter

                                                        Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

                                                          26 octobre 2018 à 10:51:47

                                                          michelbillaud a écrit:

                                                          Faut peut être revenir à la question posée, qui était de savoir comment signaler un problème pendant la construction d'un objet.

                                                          Et la réponse est simple et a déjà été donnée : à la construction (*) d'un objet, tu fais une assertion sur toutes les valeurs fournies par l'utilisateur qui pourraient faire foirer la chose.

                                                          Parce qu'il n'y a rien à faire : la construction d'un objet entre systématiquement dans le cadre d'un processus logique qui ne dépend que du développeur qui décide de créeer l'objet.  Si bien que, ce qu'il faut, c'est s'assurer que cette logique est correcte à la base avant de passer en production.

                                                          Mais, une fois que la logique est validée, que l'on a pu se rendre compte que, quel que soit le chemin suivi pour en arriver à la création de l'objet, les valeurs passées sont correcte, le test n'a plus aucune raison d'être, et peut donc être viré de la prod.

                                                          La seule exception portant sur les ressources hors du contrôle du développeur, pour lesquelles une exception devra être lancée:

                                                          • fichier absent ou incohérent
                                                          • manque de mémoire
                                                          • serveur distant indisponible / réponse négative de la part d'un serveur distant
                                                          • ... j'en passe, et peut être de meilleurs

                                                          (*) Note, d'ailleurs, que cela ne se limite pas à la seule construction d'un objet, mais à l'ensemble du projet, à toutes les données dont la transmission dépend de la logique du développeur ;)

                                                          • Partager sur Facebook
                                                          • Partager sur Twitter
                                                          Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                                                            26 octobre 2018 à 10:55:40

                                                            Ksass`Peuk a écrit:

                                                            [surcout de if() par rapport à assert()]

                                                            Je ne dis pas que l'exception c'est forcément mieux, je dis que ça peut être une hypothèse à envisager *peut-etre*, selon qu'on est dans un cas où considère que la situation est récupérable ou pas. C'est à discuter selon le contexte.

                                                            Je te rappelle que nous répondons à un _débutant_ qui fait un _exercice_. C'est pas un responsable d'exploitation qui s'arrache les cheveux pour faire tourner un million de processus sur des bécanes surchargées, et qui fait compiler ses applications avec le maximum d'options d'optimisation pour que ça rentre.

                                                            Les économies de bout de chandelle (on parle bien du coût de la comparaison d'un entier à 0 dans un constructeur...)  dans un programme qui n'est  absolument pas critique ni contraint en temps ou en espace et que personne n'utilisera de toutes façons, c'est des considérations qui ne sont pas prioritaires _pour lui_. Et ce n'était pas sa question.

                                                            -
                                                            Edité par michelbillaud 26 octobre 2018 à 10:56:07

                                                            • Partager sur Facebook
                                                            • Partager sur Twitter
                                                              26 octobre 2018 à 11:03:26

                                                              michelbillaud a écrit:

                                                              Je te rappelle que nous répondons à un _débutant_ qui fait un _exercice_.

                                                              Jamais dit le contraire. Mais je ne pense pas que "assert" soit une surcharge cognitive si importante. Et que globalement, c'est tout bénef de savoir, assez tôt, faire la différence entre une erreur dont on peut se prémunir et celle dont on ne peut pas se prémunir.

                                                              • Partager sur Facebook
                                                              • Partager sur Twitter

                                                              Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

                                                              Classe Fraction : le cas de la division par 0

                                                              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                              • Editeur
                                                              • Markdown