Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Exo de GBDivers] autre implémentation.

Ha ces fractions !

    25 septembre 2015 à 21:24:40

    L’exercice que fait en ce moment Soin… me semble intéressant, alors je m’y suis mis aussi …

    J’ai commencé sans regarder ni la solution de Soin… ni les commentaires des relecteurs. J’ai développé et testé.

    (Pour la « programmation par contrat », j’ai utilisé le ficher avec les macros que RPGamer m’avait proposé).

    Puis j’ai pris en compte les premiers commentaires, (un nom de namespace (mes initiales,;-)), le manque de quelques opérateurs (>, <, et << que je n’avais pas pensé).

    Il me reste quelques points pour finir :

    • ·Comme c’est une classe à sémantique de donnée, j’aurai voulu interdire l’héritage, mais je n’ai pas trouver comment !
    • ·Vue le choix d’utilisation de « template », je ne sais pas comment faire le contrôle de l’over flow.
    • ·Il faudrait que je mette en forme mes tests unitaires (c’est pas encore présentable)

    J’ai mis mes fichiers sur https://github.com/Dedeun/ExoFraction.git

    Si vous n’en avez pas trop mare de regarder des fractions, merci de me dire ce vous en pensez. Je ne sais pas ce que ça faut !

    (Soin… je ne fais pas de concours, je ne suis pas parti dans le même sens que toi, et ça ne se ressemble pas)

    • Partager sur Facebook
    • Partager sur Twitter
      25 septembre 2015 à 22:08:39

      classe toto final { ...

      Premier passage.

      HS : question piège, pour Soin... aussi (pour la réflexion, pas à implémenter) :

      Fraction<T> operator-= (Fraction<T> const& f)
      {
          m_num = (m_num * f.m_den) - (f.m_num * m_den);
          m_den = m_den*f.m_den;  <====== ICI
          CHECK_INVARIANTS;
          reduction();
          CHECK_INVARIANTS;
          return (*this);
      }
      
      Fraction a, b;
      try {
          a -= b;
      } catch(...) { // bouh, c'est moche
          cout << a;
      }

      Imagine que j'écrive le code avec le try-catch. Je souhaite que si une exception est lancée dans l'opérateur -=, la valeur de la fraction ne soit pas modifée (strong garantie expcetion).

      Comment modifier le code pour éviter cela ? En particulier si une exception est lancée à la ligne "ICI"

      -
      Edité par gbdivers 25 septembre 2015 à 22:13:32

      • Partager sur Facebook
      • Partager sur Twitter
        26 septembre 2015 à 7:43:24

        Merci GBDivers pour cette relecture rapide.  :)

        • Pour "final", ok je vais essayer (j'avais trouver en effet ce mot, mais dans les exemples il était associé à "virtual", et là je ne comprend pas! (exemple
        struct A
        {
            virtual void foo() final; // A::foo is final
        ....

        comment être à la fois "virtual" et "final" ?

        • Merci pour la classe numeric_limits, à voir ce que je peux faire avec :euh: ... A suivre
        • Ok pour les valeurs par défaut du constructeur (1/1 me semble la meilleure valeur) :p
        • En ce qui concerne l'utilisation de "reduction" si m_den ==1, c'est discutable :pirate: , l'algorithme que j'ai trouvé sur le net de PGDC, va sortir très rapidement (1 ou 2 tours de boucle) suivie d'une division par 1. Je ne suis pas sûr que l'ajout d'un "if" ne face pas perdre en simplicité d'algorithme. (Mais, je note ta remarque) (en fait, dans une version précédente du code, j'avais un cas particulier pour les valeurs 1 en dénominateur, mais c'était bugé ... je me suis rendu compte quand supprimant ce cas particulier, c'était bon ... "alors pourquoi faire compliqué quand on peut faire simple!")
        • Par contre, je ne comprend pas ta remarque au sujet de isEqualAs() et IsGreaterThan() :euh: . En effet, je les utilise dans l’opérateur == et >. J'ai fait un peu comme pour réaliser l’opérateur libre "+" où j'ai utilisé l'opérateur de classe "+=". isEqualAs() et IsGreaterThan() sont mes opérateurs de classe, qui me permettent de réaliser mes opérateurs libres <, <=, > et >=.

        Merci GBDivers.

        Si vous avez aussi un avis sur les commentaires, ...  (c'est la première fois que je "fais" du doxygen et je suis pas convaincu!).

        • Partager sur Facebook
        • Partager sur Twitter
          26 septembre 2015 à 11:48:18

          gbdivers a écrit:

          HS : question piège, pour Soin... aussi (pour la réflexion, pas à implémenter) :

          Dans le cas d'une fraction template, je trouve que la strong-guarantee est intéressante à implémenter.

          @Dedeun : Pour le contrôle d'overflow, j'ai mis des indices dans le post de @Soin... . Pour les opérateur "op=", le type de retour n'est pas le bon dans ton implémentation, tu peux regarder l'implémentation canonique des opérateurs dans le lien filé par @gbdivers, tu vas pas mal simplifier tes écritures.

          • Partager sur Facebook
          • Partager sur Twitter

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

            26 septembre 2015 à 12:35:14

            Merci Ksass'Peuk;

            Ksass`Peuk a écrit:


            @Dedeun : Pour le contrôle d'overflow, j'ai mis des indices dans le post de @Soin... .

             C'est en effet en regardant la réponse que tu avais fait à Soin... que j'ai penser que le contrôle de l'overflow manquait. Tu proposes (sauf erreur de ma part) d'utiliser la division du résultat pour vérifier que la multiplication est correct. Je n'ai pas encore trouvé comment faire pour l’implémenter de façon "élégante".

            Pour la forme canonique de OP==, je crois que j'ai compris mais si PB, je reviens vers vous.

            Merci pour tes remarques.

            -
            Edité par Dedeun 26 septembre 2015 à 19:05:29

            • Partager sur Facebook
            • Partager sur Twitter
              26 septembre 2015 à 18:00:27

              Dedeun a écrit:

              comment être à la fois "virtual" et "final" ?

              Évite de faire cela. (c'est un hack trop moche pour interdire la surcharge)

              Dedeun a écrit:

              • (1/1 me semble la meilleure valeur)

              0/1 me semble plus pertinent

              Dedeun a écrit:

              • En ce qui concerne l'utilisation de "reduction" si m_den ==1, c'est discutable :pirate: , l'algorithme que j'ai trouvé sur le net de PGDC, va sortir très rapidement (1 ou 2 tours de boucle) suivie d'une division par 1. Je ne suis pas sûr que l'ajout d'un "if" ne face pas perdre en simplicité d'algorithme. (Mais, je note ta remarque) (en fait, dans une version précédente du code, j'avais un cas particulier pour les valeurs 1 en dénominateur, mais c'était bugé ... je me suis rendu compte quand supprimant ce cas particulier, c'était bon ... "alors pourquoi faire compliqué quand on peut faire simple!")

              Le calcul est vite fait : d'un côté, tu as un constructeur qui ne fait rien s'il prend comme dénominateur 1 (pas de if, pas d'appel à reduction).

              As tu une implémentation qui est plus rapide que "rien faire" ?

              (EDIT : mais en fait, on s'est peut être pas compris. Je ne parlais pas de tester si denom == 1 dans le constructeur qui prend 2 paramètres, mais d'avoir un constructeur à 0 et 1 paramètre, qui fixe denom == 1 et qui n'a donc pas besoin de réduction. La résolution de cet appel se fait à la compilation)

              • Partager sur Facebook
              • Partager sur Twitter
                26 septembre 2015 à 23:01:32

                Bonsoir,

                Je viens de mettre à jour le repository sur Github, avec mes derniers sources: Je pense avoir pris en compte vos remarques, j'y ai ajouté le fichier main avec les tests unitaires.

                https://github.com/Dedeun/ExoFraction

                • Ajout de final après le nom de la classe,
                • Ajout d'un constructeur avec un seul argument et une valeur par défaut
                • Suppression de isGreaterTan() et isEqualTo(), remplacées directement par op== et op>
                • Ajout de contrats "Précondition" pour le contrôle de l'overflow (pas trop élégant: double calcule) ... mais, sauf erreur de ma part, répond à la demande: lève l'Assert avant la modification de la valeur.

                En ce qui concerne les tests unitaires, je n'ai pas compris comment faire pour les jouer tous en une seul fois, en effet à chaque assert, le soft s'arréte, je mets en commentaire la ligne (ou le test) qui a levé l'assert, et je relance ... (J'ai regardé pour utiliser Boost/tests, mais "il"dise qu'il ne faut pas d'assert dans les tests)

                Si vous avez encore quelques minutes, merci pour vos remarques (et en particulier sur les commentaires doxygen).

                -
                Edité par Dedeun 26 septembre 2015 à 23:02:54

                • Partager sur Facebook
                • Partager sur Twitter
                  27 septembre 2015 à 0:53:57

                  Il manque un check sur ton type template. A priori, il faut que T soit un type entier. Utilise static_assert et is_integral. Comme cela, si quelqu'un essaie d'utilise ta classe avec n'importe quoi, il aura l'info.

                  static_assert(std::is_integral<T>::value, "Integer required.");

                  Note : un message d'erreur non explicite - en particulier avec les templates - peut être considéré comme un bug. Si un utilisateur tente d'utiliser ton code et reçoit une erreur sans la comprendre, c'est un problème dans ton code. Et ne pointe pas du doigt les messages d'erreur de la STL, ce n'est pas poli de montrer du doigt...

                  Il y a des problème avec tes contrats (par exemple) :

                      Fraction<T> operator+= (Fraction<T> const& f)
                          {
                              REQUIRE((((m_num * f.m_den)/f.m_den)==m_num), "Detection of overflow");
                              REQUIRE((((f.m_num * m_den)/m_den)==f.m_num), "Detection of overflow");
                              m_num = (m_num * f.m_den) + (f.m_num * m_den);
                              m_den = m_den*f.m_den;
                              CHECK_INVARIANTS;
                              reduction();
                              CHECK_INVARIANTS;
                              return (*this);
                          }
                  • tu fais des calculs sur des types entiers, donc tes opérations (dans tes calculs de conditions) seront des opérations entières et seront fausses
                  • tu appelles CHECK_INVARIANTS après avoir modifié num et den, mais avant reduction. Un invariant se vérifie au début d'une fonction (pour savoir si la classe est dans un état valide avant de commencer à la modifier) et à la fin (pour savoir si les calculs laissent la classe dans un état valide). Entre les 2, on est autorisé à avoir un état invalide. 

                  Pour les tests, le principe de base est simple. Comment veux tu tester si un code d'une fonction est correct ? Tu écris un main.cpp qui utilise ce code. Puis tu le compiles et regardes si tu as des messages d'erreurs. Puis tu l'exécutes et regardes si tu as des messages d'erreurs ou si le comportement est faux.

                  Une série de tests est simple un script qui va faire tourner tous les tests et vérifier s'il n'y a pas d'erreur de compile ou de runtime (en lisant la sortie standard pour trouver les messages d'erreur).

                  Pour éviter de devoir écrire 100 main.cpp, les frameworks de tests catach les signaux et exceptions pour faire tourner plusieurs tests dans un main, mais l'idée de base est là.

                  Tu peux peut être tester des sites de tests en ligne, par exemple https://travis-ci.org/

                  • Partager sur Facebook
                  • Partager sur Twitter
                    27 septembre 2015 à 0:55:05

                    HS : on a parlé de strong garantie exception. 

                    • sais tu ce que c'est ?
                    • sais tu pourquoi tu n'as pas cette garantie dans le code que j'ai indiqué ?
                    • sais tu comment corriger cela ?

                    Remarque sur les tests : un point de départ pour les écrire est de prendre en compte les retours de "bugs" que l'on te fait sur un code. Si par exemple on te dit (nous dans cet exo, mais cela sera peut être un client dans /quelques temps) "si j'écris 4/2*7/3, j'obtiens une erreur", tu peux écrire un test pour ce problème.

                    (Le TDD - test driving dev - dit même qu'il faut écrire en premier le test, puis le code)

                    (HS note pour plus tard : template specialisation pour paramétrer l'exception garantie)

                    -
                    Edité par gbdivers 27 septembre 2015 à 1:08:41

                    • Partager sur Facebook
                    • Partager sur Twitter
                      27 septembre 2015 à 10:37:26

                      Merci GBDivers,

                      gbdivers a écrit:

                      Il manque un check sur ton type template. A priori, il faut que T soit un type entier. Utilise static_assert et is_integral. Comme cela, si quelqu'un essaie d'utilise ta classe avec n'importe quoi, il aura l'info.

                      En effet, j’avais identifié qu’il y avait une lacune, mais je ne savais pas faire (C’est la première fois que j’utilise des tempate (et des assert aussi, d’ailleur))

                      gbdivers a écrit:

                      Il y a des problème avec tes contrats (par exemple) :

                      • tu fais des calculs sur des types entiers, donc tes opérations (dans tes calculs de conditions) seront des opérations entières et seront fausses
                      • tu appelles CHECK_INVARIANTS après avoir modifié num et den, mais avant reduction. Un invariant se vérifie au début d'une fonction (pour savoir si la classe est dans un état valide avant de commencer à la modifier) et à la fin (pour savoir si les calculs laissent la classe dans un état valide). Entre les 2, on est autorisé à avoir un état invalide. 

                      Ce choix vient des tests que j’avais fait : Dans l’un des tests, (la division si je me souviens bien), on peut avoir un dénominateur nul (c’était avant que j’ajoute la précondition), et alors l’algo de réduction plante ! C’est pour ça que j’avais ajouté le test d’invariant avant la réduction.

                      gbdivers a écrit:

                      Pour les tests, le principe de base est simple. Comment veux tu tester si un code d'une fonction est correct ? Tu écris un main.cpp qui utilise ce code. Puis tu le compiles et regardes si tu as des messages d'erreurs. Puis tu l'exécutes et regardes si tu as des messages d'erreurs ou si le comportement est faux.

                      Pour les tests, je pense avoir fait quelque chose qui ressemble à ce que tu dis : Une boucle de 8 tests qui testent les différentes valeurs des paramètres, pour les différents algorithmes. Il y a des tests fonctionnels (avec des valeurs dans le range des donnée), des tests « aux bornes » (avec les valeurs 1/1 et 0/1), et des tests hors range (avec des valeurs de dénominateur 2puissance30). J’ai activé tous les fonctions et tous les types de assert, j’ai une couverture de test qui est à la fois (presque) complète et efficace.

                      Je regrette seulement de ne pouvoir les jouer en une seul fois, l’assert stop le programme, pour passer au test suivant, il faut modifier le code de tests ; Pour les tests de non régression, c’est pas Cool !

                      gbdivers a écrit:

                      HS : on a parlé de strong garantie exception. 

                      • sais tu ce que c'est ?
                      • sais tu pourquoi tu n'as pas cette garantie dans le code que j'ai indiqué ?
                      • sais tu comment corriger cela ?

                      J’ai trouvé l’explication ici : https://en.wikipedia.org/wiki/Exception_safety

                      Dans le code que j’ai écrit, (là où tu as « Ici »), il ne devrait pas y avoir d’exception, c’est une simple multiplication, … Mais dans le cas où … A la ligne précédente, on a déjà commencé à modifier l’objet, et si un assert (ou une exception) tombe on a fait que moitié du travail.

                      Suivant moi (je ne sais pas où chercher « l’implémentation recommandée », alors j’invente), il faudrait faire l’opération sur une copie de l’objet, faire toutes les vérifications d’intégrités, et finalement faire la copie de cette objet temporaire dans l’objet final. Soit …

                      Fraction<T> operator-= (Fraction<T> const& f)
                      {
                          CHECK_INVARIANTS;
                          Fraction<T> tmp ;
                          tmp.m_num = (m_num * f.m_den) - (f.m_num * m_den);
                          tmp.m_den = m_den*f.m_den;
                          tmp.reduction();
                          CHECK_INVARIANTS;
                          *this = tmp ;
                          return(*this);
                      }
                      Première conclusion sur l’exercice : La « programmation par contrat » par des assert, c’est vraiment de la grosse artillerie : Pas facile dans les tests, Macro, …

                      Tu proposes GBDivers de recommencer cette librairie en utilisant les codes « NaN » et « Inf ». Dans ce cas, il n’y aura plus d’assert et de « programation par contrat », en effet toutes les opérations sont maintenant possibles (o_Osauf peut-être l’overflow, --> Faut-il ajouter un code « OF » comme 3ème code spécifiques ?).

                      Suivant le lien wikipédia, cette nouvelle classe serait « No-throw guarantee, also known as failure transparency ».   Isn’t it ? (comme il dise outre manche ou atlantique)  :) . On reporte les difficultés sur la classe qui appel la librairie, et qui doit prendre en compte les codes d’exception.

                       Merci pour ces remarques, je vais continuer --> Hop on passe à la seconde phase.

                      -
                      Edité par Dedeun 27 septembre 2015 à 10:40:49

                      • Partager sur Facebook
                      • Partager sur Twitter
                        27 septembre 2015 à 12:16:25

                        Dedeun a écrit:

                        Dans le code que j’ai écrit, (là où tu as « Ici »), il ne devrait pas y avoir d’exception, c’est une simple multiplication, … Mais dans le cas où … A la ligne précédente, on a déjà commencé à modifier l’objet, et si un assert (ou une exception) tombe on a fait que moitié du travail.

                        Ta classe fraction est template. Si je décrète qu'en type paramètre d'entrée je met "Z" le type des entiers relatifs de taille arbitrairement grande, c'est bien un type "integral". Derrière, l'implémentation utilisera très certainement un tableau dont la taille s'accroît quand le nombre grandit, et donc risque de lever une exception à l'allocation sur la multiplication.

                        • Partager sur Facebook
                        • Partager sur Twitter

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

                          27 septembre 2015 à 15:16:49

                          Yop,

                          Bon j'ai pas tout lu :p mais je viens répondre aux quelques questions de @gbdivers

                          gbdivers a écrit:

                          • sais tu ce que c'est ?
                          • sais tu pourquoi tu n'as pas cette garantie dans le code que j'ai indiqué ?
                          • sais tu comment corriger cela ?


                          Pour moi, d'après ce que j'ai compris sur stack overflow, la strong guaranties exception est, une exception qui permet de ne pas modifier la valeur d'une variable si jamais un problème est trouvé, par exemple, si jamais une opération est apporté sur une date "02/04/2015" et passe à "38/04/2015", ici, le contrat est rompu (il n'existe aucun mois avec 38 jours) et donc la date est fausse du coup le programme laissera 02/04/2015 comme si la date n'a pas été modifiée.

                          A mon avis nous n'avons pas cette guarantie car nous allors arrêter le programme lorsque le contrat sera rompu (en gros on est sur une Basic strong guarantie si je comprends bien).

                          Pour corriger cela, il suffit d'ajouter un throw à la place du check des invariants (je n'ai pas testé, mais à mon avis c'est ça :p ), qui lui, annulerait l'opération si jamais le contrat est rompu.

                          Personnellement je trouve que c'est une bonne idée de faire une classe templaté pour les fractions (même si dans ce cas là, il faut faire attention parce que tu ne peux pas avoir de nombre décimal dans une fraction), et aussi je trouve que implémenter les membres dans la classe elle-même est pas super lisible (totalement subjectif).

                          PS : tu viens de me faire penser à implémenter les opérateurs x= dans le code, j'avais complètement oublié :p

                          Cordialement,

                          • Partager sur Facebook
                          • Partager sur Twitter
                            27 septembre 2015 à 15:31:11

                            Dedeun a écrit:

                            Ce choix vient des tests que j’avais fait : Dans l’un des tests, (la division si je me souviens bien), on peut avoir un dénominateur nul (c’était avant que j’ajoute la précondition), et alors l’algo de réduction plante ! C’est pour ça que j’avais ajouté le test d’invariant avant la réduction.

                            Attention à ce genre de démarche. L'une des plus grosses plaies (probablement) en dév est de mal comprendre les intentions des autres devs.

                            Si tu avais une autre fonction qui s'appelle "calculer_theme_astral" qui fait exactement la même chose que ta fonction "reduction", tu l'utiliserais dans ton code de Fraction ? Probablement pas. Parce que même si c'est le même code, le nom de la fonction pourrait être source de confusion.

                            Un invariant a pour but de vérifier l'état d'un objet. Point. Même s'il fait ce que tu as besoin pour appeler "reduction", ce n'est pas son role. Donc tu ne dois pas l'utiliser pour cela. Au pire, tu refactorises pour avoir un fonction "check_denom_null" que tu utilises dans ton invariant et avant d'appeler "reduction", mais il ne faut pas utiliser ton invariant pour cela.

                            Par contre, cela montre une faiblesse dans la vérification de tes contrats : tu ne le fais pas pour tes fonctions privées. Si réduction DOIT avoir un denom non null, alors, tu dois vérifier cela dans reduction.

                            Dedeun a écrit:

                            Pour les tests, je pense avoir fait quelque chose qui ressemble à ce que tu dis : Une boucle de 8 tests qui testent les différentes valeurs des paramètres, pour les différents algorithmes. Il y a des tests fonctionnels (avec des valeurs dans le range des donnée), des tests « aux bornes » (avec les valeurs 1/1 et 0/1), et des tests hors range (avec des valeurs de dénominateur 2puissance30). J’ai activé tous les fonctions et tous les types de assert, j’ai une couverture de test qui est à la fois (presque) complète et efficace.

                            Je regrette seulement de ne pouvoir les jouer en une seul fois, l’assert stop le programme, pour passer au test suivant, il faut modifier le code de tests ; Pour les tests de non régression, c’est pas Cool !

                            Pour les tests, c'est effectivement une bonne base.

                            Par contre, remarque : on peut faire autant de tests que l'on veut, on ne couvre jamais 100% des cas.

                            Sans framework de test, c'est un peu plus compliqué de faire tourner cela tout automatique (il faut écrire les scripts qui font tourner les tests et lisent les résultats). As tu regardé travisCI ?

                            Je n'ai jamais trop regardé le détail de l'implémentation des frameworks de tests, mais probablement qu'ils catchent toutes les exceptions et tous les signaux. Il faudrait regarder cela en détail (ou si quelqu'un n'a pas déjà implémenter cela ?)

                            Dedeun a écrit:

                            Suivant moi (je ne sais pas où chercher « l’implémentation recommandée », alors j’invente), il faudrait faire l’opération sur une copie de l’objet, faire toutes les vérifications d’intégrités, et finalement faire la copie de cette objet temporaire dans l’objet final. Soit …

                            +1, c'est l'idée.

                            Maintenant, les subtilités ;) 

                            Une fois que tu as fait les calculs et mis le résultat dans ta variable temporaire, tu es en droit de considérer que le reste n'est que de la finalisation, qui ne doit pas poser de problème particulier. Si un problème doit survenir (exception), tu t'attends à ce que ce soit lors des calculs, pas lors de la copie de temp vers this. (D'autant plus que ta version sans strong exception garantie ne fait pas de copie et donc ne lance pas d'exception sur la copie, tu veux probablement que ta version avec la strong ait exactement le même comportement).

                            Or, tu fais une copie en plus dans ta version avec strong. Es tu sur que cette copie ne peut pas lancer d'exception ?

                            Si tu prends pas exemple :

                            v = vector<int>(100);

                            Il peut y avoir allocation dans v si la taille du buffer interne est trop petite. Et donc risque d'exception. Ksass`Peuk a donné un exemple de classe d'entiers qui peut lever des exceptions aussi. Donc la copie n'est pas toujours garantie sans exception.

                            Il est donc préférable d'utiliser une fonction qui ne lance pas d'exception (noexcept) à ce niveau. Meyers (Effective Modern C++) recommande :

                            noexcept is particularly valuable for the move operations,
                            swap, memory deallocation functions, and destructors. 

                            Donc : 1. ajoute noexecpt à ces fonctions (en vérifiant qu'elles sont bien noexecpt). 2. utilise swap pour le tmp :

                            *this = tmp ;
                            // devient
                            std::swap(tmp, *this);
                            // ou 
                            this->swap(tmp)

                            Pour swap : http://en.cppreference.com/w/cpp/concept/Swappable et http://en.cppreference.com/w/cpp/language/extending_std

                            Dedeun a écrit:

                            Première conclusion sur l’exercice : La « programmation par contrat » par des assert, c’est vraiment de la grosse artillerie : Pas facile dans les tests, Macro, …

                            Alors, oui, mais n'oublie pas que tu n'utilises pas de framework de tests, ce qui rend les choses plus compliquées.

                            En pratique, la programmation par contrat (comme beaucoup d'autres choses de qualité logiciel), tu fais cela sur les codes les plus critiques (les classes de base, qui seront réutilisées partout). Pour des raisons de contraintes de temps généralement.

                            Et en pratique, la programmation par contrat, c'est quelque chose que tu maudis de ne pas avoir fait, lorsque tu as passé 2 semaines à débugger un code, qui aurait pu être éviter si tu avais respecté la programmation par contrat...

                            Bref, on en fait pas des contrats systématiquement, et on le regrette très souvent.

                            EXO 1 bis (template specialisation)

                            Avant d'aller plus loin avec l'exo 2 (qui en fait n'est pas fondamentalement plus compliqué... et comme tu t'es bien débrouillé avec les templates). La strong garantie exception est un peu plus lourde que sans cette garantie, puisque cela impose de travailler sur un temporaire. Dans certains cas, je sais que je n'aurais pas de problème avec les exceptions, du coup, j'aimerais pouvoir utiliser la strong par défaut, mais pouvoir la desactiver si j'ai envie. Et bien sur sans changer le code (donc avec 1 classe, pas 2)

                            Faction<MyInt> f; // strong garantie exception
                            
                            Faction<int, no_strong> f; // no garantie exception

                            Comment faire cela ?

                            Dedeun a écrit:

                            Tu proposes GBDivers de recommencer cette librairie en utilisant les codes « NaN » et « Inf ». Dans ce cas, il n’y aura plus d’assert et de « programation par contrat », en effet toutes les opérations sont maintenant possibles (o_Osauf peut-être l’overflow, --> Faut-il ajouter un code « OF » comme 3ème code spécifiques ?).

                            Suivant le lien wikipédia, cette nouvelle classe serait « No-throw guarantee, also known as failure transparency ».   Isn’t it ? (comme il dise outre manche ou atlantique)  :) . On reporte les difficultés sur la classe qui appel la librairie, et qui doit prendre en compte les codes d’exception.

                            Oui.

                            Il y a deux façons de gérer les "cas limites" : soit on les refuse (et on vérifie cela avec des assert pour crasher si on essaie d'utiliser des cas interdit), soit on les acceptes (et il faut gérer ces cas limites pour que la classe conserver un comportement prévisible et prévu). La second approche est plus lourde à développer, généralement moins performante, mais plus résistante aux utilisateurs.

                            Remarque : c'est ce que tu fais pour les dénominateur négatifs dans ta classe Fraction. Tu as écrit :

                            if (den > 0) {
                                m_den=den;
                            } else {
                                m_den=-den;
                                m_num *=-1;
                            } //end if

                            Donc tu autorises l'utilisation de den négatif par l'utilisateur, et corrige en interne le signe du denom. C'est un choix. Mais cela implique que ton code perd un peu de performance (un test). Si j'ai la garantie d'appeller Fraction uniquement avec des denom > 0, ton code posera problème (j'ai 1 million de fraction à analyser, dont tous les denom sont > 0... ton if fait perdre beaucoup de temps).

                            En C++, on est un peu maniaque (les gens normaux disent "un peu à l'ouest", les devs Java disent "un peu con". Tout dépend du point de vue :) ) sur l'overhead (surcout d'utilisation), surtout si celui-ci peut être éviter. On préfère généralement avoir une version basique sans overhead.

                            • Partager sur Facebook
                            • Partager sur Twitter
                              27 septembre 2015 à 17:16:43

                              Ksass'Peuk > Ta classe fraction est template. Si je décrète qu'en type paramètre d'entrée je met "Z" le type des entiers relatifs de taille arbitrairement grande, c'est bien un type "integral". Derrière, l'implémentation utilisera très certainement un tableau dont la taille s'accroît quand le nombre grandit, et donc risque de lever une exception à l'allocation sur la multiplication.

                              C'est possible ca ? j'savais pas ! Merci pour la remarque (Je pensai qu'on se limité à "int16_t", int32_t" et "int64_t" ...) Surprise! o_O

                              Soin... > ... et aussi je trouve que implémenter les membres dans la classe elle-même est pas super lisible (totalement subjectif)

                              D’accord avec toi, ... mais pas le choix (sauf erreur de ma part). De même pour le "template<typename T>" c'est pas cool!

                              GBDivers > ...Par contre, cela montre une faiblesse dans la vérification de tes contrats : tu ne le fais pas pour tes fonctions privées. Si réduction DOIT avoir un denom non null, alors, tu dois vérifier cela dans reduction.

                              Il est possible donc d'ajouter des contrats dans mes methodes privées ? :o Je pensais que c'était réservé aux interfaces externes!  ("Tu ne m'as pas appelé avec des argument valides, le bug est chez toi." ou "Malgré que tes arguments sont valides, la sortie de ma classe n'est pas correct, le bug est chez moi").

                              Mais de toute manière, sortir brutalement du soft, ça ne permet pas facilement de débuger ... et dans mon cas de faire des tests de non reg. Je confirme, ca me parait lourd (une exception serait sûrement plus facile à catcher/tester)

                              GBDivers > ... As tu regardé travisCI ?

                              Je n'ai jamais trop regardé le détail de l'implémentation des frameworks de tests, mais probablement qu'ils catchent toutes les exceptions et tous les signaux. Il faudrait regarder cela en détail (ou si quelqu'un n'a pas déjà implémenter cela ?)

                              Non, pas encore eu le temps de regarder travisCI (il n'y a que 24 petites heures pour chaque journée du Week-end; c'est court, face au longues journée de 24 heures de la semaines! :D ). Ce que j'ai lu sur boost/test, c'était que le framework de tests boost "catcher" toutes les exception, ... mais pas les assert. Donc on est dans le même cas que mon minuscule automate. Si je test un contrat avec assert dans un test, je sort du programme de tests. :(

                              GBDivers > EXO 1 bis (template specialisation)

                              (Et zut, moi qui penser en avoir fini avec les fractions .... J'ai jamais aimé ça, à l'école!) ;)  Bon aller, je vais y réfléchir ...

                              J'avais déjà vu cette fonction swap sans rien y comprendre, ... je vais regarder ça de plus près.

                              GBDiuvers > Il y a deux façons de gérer les "cas limites" : soit on les refuse (et on vérifie cela avec des assert pour crasher si on essaie d'utiliser des cas interdit), soit on les acceptes (et il faut gérer ces cas limites pour que la classe conserver un comportement prévisible et prévu). La second approche est plus lourde à développer, généralement moins performante, mais plus résistante aux utilisateurs.

                              J'ai surtout l'impression qu'on laisse a l'utilisateur la gestion des cas limites (ici  NaN, Inf et OF), qui ne sont pas triviaux: Que doit faire l'utilisateur si une opération retourne NaN, ... Pour ma part, j'ai surtout l'impression qu'on lui passe la "patate chaude", à lui de s'en démerder ("Non, non, ça c'est l'application qui sait ce quel doit faire dans ce cas là, pas la plateforme, ...")

                              -
                              Edité par Dedeun 27 septembre 2015 à 21:05:18

                              • Partager sur Facebook
                              • Partager sur Twitter
                                28 septembre 2015 à 18:58:22

                                >sortir brutalement du soft, ça ne permet pas facilement de débuger

                                What ??? Les coredumps sont nos amis.

                                • Partager sur Facebook
                                • Partager sur Twitter
                                Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                                  29 septembre 2015 à 1:03:00

                                  Dedeun a écrit:

                                  C'est possible ca ? j'savais pas ! Merci pour la remarque (Je pensai qu'on se limité à "int16_t", int32_t" et "int64_t" ...) Surprise! 

                                  En fait, c'est toi qui décide. En fonction de ton static_assert(is_integral<T>::value>. is_integrale = n'importe quel type similaire à un entier (pas sur de la traduction...). Si tu utilise is_fondamental, tu n'auras qu'un type fondamentale du C++ (int, int8, etc). Si tu utilises is_numeric, cela acceptera n'importe quel type numérique (int, float, etc). Et tu peux mettre plusieurs tests. Bref, tu choisis ce que tu acceptes ou non pour T.

                                  Dedeun a écrit:

                                  mais pas le choix (sauf erreur de ma part). De même pour le "template<typename T>" c'est pas cool!

                                  Quoi qui est "pas cool" ?

                                  Il faut déclarer dans la classe, mais vous pouvez définir les fonctions après (ou dans un autre fichier, mais c'est bof).

                                  Dedeun a écrit:

                                  Il est possible donc d'ajouter des contrats dans mes methodes privées ? :o Je pensais que c'était réservé aux interfaces externes!  ("Tu ne m'as pas appelé avec des argument valides, le bug est chez toi." ou "Malgré que tes arguments sont valides, la sortie de ma classe n'est pas correct, le bug est chez moi").

                                  Pour l'interface public, les tests sont plus critiques. Mais la partie privée n'est pas protégée contre les erreurs de programmation. Donc oui, tu peux faire des contrats dessus (mais plus léger, moins documenté. On est plus sur de la doc interne)

                                  Dedeun a écrit:

                                  (une exception serait sûrement plus facile à catcher/tester)

                                  Nop. Evites les mauvaises pratiques : erreur de programmation = assert, situation exceptionnel (mais prévisible, comme une coupure réseau, erreur de lecture de fichier, etc) = exception, retourner un objet invalide = avec beaucoup de précaution, les utilisateurs ne vérifie pas souvent l'invalidité des données retournées.

                                  Dedeun a écrit:

                                  Non, pas encore eu le temps de regarder travisCI 

                                  Je vous prépare quelque chose pour simplifier les tests et le boulot sur les exos. Quelques jours probablement.

                                  Dedeun a écrit:

                                  J'ai surtout l'impression qu'on laisse a l'utilisateur la gestion des cas limites (ici  NaN, Inf et OF), qui ne sont pas triviaux: Que doit faire l'utilisateur si une opération retourne NaN, ... Pour ma part, j'ai surtout l'impression qu'on lui passe la "patate chaude", à lui de s'en démerder ("Non, non, ça c'est l'application qui sait ce quel doit faire dans ce cas là, pas la plateforme, ...")

                                  C'est deux démarches différentes. Avec un assert, on dit "c'est interdit, je crash". Avec une exception, on dit "Il y a quelque chose de pourri au royaume du Danemark. Vérifies tes données et réessaies". Avec le troisième cas (objet valide au sens de l'invariant de classe, mais invalide en termes de données représentées, comme c'est le cas avec Inf et Nan), on s'intéresse pas à la validation des données, on veut le résultat et on analysera après (Inf et Nan sont un résultat dans ce sens)

                                  bacelar a écrit:

                                  >sortir brutalement du soft, ça ne permet pas facilement de débuger

                                  What ??? Les coredumps sont nos amis.

                                  Je suis pas fan en général des coredump :)

                                  Pour les tests, cela est plus compliqué à analyse qu'un simple message dans les logs, qui sera lu par un système de CI et qui affichera le nombre de tests qui ont réussit, qui ont planté, qui ont eu des warnings, etc.

                                  Pour une vraie application, on est vite limité quand l'application est chez le client (il n'envoie pas toujours le fichier de log, du coup un coredump faut même pas en parler). Et c'est quasi impossible dans certains cas (Android = dur. iOS = au secours. Sans compter qu'il faut passer le téléphone en mode dev)

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    29 septembre 2015 à 17:18:28

                                    >Pour les tests, cela est plus compliqué à analyse qu'un simple message dans les logs

                                    Pour les tests, c'est soit un exécutable spécial qui catche et log, soit l'exécutable final mais avec un watchdog qui log => ça change rien pour le CI, et le code est près sans modification pour la release.

                                    >(il n'envoie pas toujours le fichier de log,

                                    Le watchdog n'est pas forcement très diplomate et peut cafter sans la permission de l'utilisateur.

                                    Sous Windows WER (https://en.wikipedia.org/wiki/Windows_Error_Reporting) et je crois qu'il y a l'équivalent sur les autres plateformes (au moins en outils tierce).

                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                    Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                                      29 septembre 2015 à 18:00:15

                                      Bonsoir Bacelar,

                                      Merci pour ta remarque:

                                      bacelar a écrit:

                                      >sortir brutalement du soft, ça ne permet pas facilement de débuger

                                      What ??? Les coredumps sont nos amis.

                                      Je pensais "... ça ne permet pas facilement de (débuger) tester!" J'ai en tête les batteries de test de non régression.

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        29 septembre 2015 à 18:36:39

                                        Dedeun a écrit:

                                        Bonsoir Bacelar,

                                        Merci pour ta remarque:

                                        bacelar a écrit:

                                        >sortir brutalement du soft, ça ne permet pas facilement de débuger

                                        What ??? Les coredumps sont nos amis.

                                        Je pensais "... ça ne permet pas facilement de (débuger) tester!" J'ai en tête les batteries de test de non régression.


                                        Ben, a priori, tes tests sont exactement comme ton application : un chateau lego...  Si tu testes tes fonctionnalités de base (rappel : on passe 80% du temps dans 20 % du code, même si les chiffres varient parfois ;) ), tu peux ne tester que "ce qui fait le joint" entre ces fonctionnalités au niveau des fonctionnalités plus complexes, puis que ce qui fait le lien entre les différentes fonctionnalités plus complexes et les fonctionnalités plus complexes encore qui les utilisent et ainsi de suite.

                                        Bien sur, cela sous entend d'avoir très clairement respecté le SRP, ne serait-ce qu'au niveau des fonctions, afin de pouvoir tester toutes les fonctions de manière unitaire afin d'être en mesure de définir "ce qui coince" et "où ca".

                                        Et bien sur, plus tu feras claquer tes tests de non régression "près du noyau", plus le changement apporté s'avèrera mauvais ;)

                                        Un problème en période de tests, qu'il soient automatisés (tests unitaire) ou non (tester faisant passer le "test du singe" à ton application) est compréhensible et ne devrait jamais poser énormément de problèmes quant à savoir où il se situe dans le code.

                                        Après, il y a quelques problèmes (je pense entre autres à tous les problèmes relatifs à l'utilisation des pointeurs nus) qui sont beaucoup plus complexes à résoudre, car l'endroit du crash est rarement proche de celui où le problème trouve son origine.

                                        Mais c'est justement la raison pour laquelle les pointeurs intelligents sont apparus et pour laquelle on conseille de plus en plus souvent de partir du principe qu'un pointeur nu est un pointeur qui ne sera plus que "utilisé" par la fonction, et que la fonction manipulant un pointeur nu ne doit plus s'occuper de la gestion de la mémoire.

                                        • 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
                                          29 septembre 2015 à 22:42:49

                                          Bonsoir,

                                          gbdivers a écrit:

                                          ... EXO 1 bis (template specialisation)

                                          Avant d'aller plus loin avec l'exo 2 (qui en fait n'est pas fondamentalement plus compliqué... et comme tu t'es bien débrouillé avec les templates). La strong garantie exception est un peu plus lourde que sans cette garantie, puisque cela impose de travailler sur un temporaire. Dans certains cas, je sais que je n'aurais pas de problème avec les exceptions, du coup, j'aimerais pouvoir utiliser la strong par défaut, mais pouvoir la desactiver si j'ai envie. Et bien sur sans changer le code (donc avec 1 classe, pas 2)

                                          Faction<MyInt> f; // strong garantie exception
                                          
                                          Faction<int, no_strong> f; // no garantie exception

                                          Comment faire cela ?

                                          Exercice fait à 50% :"strong garantie exception", c'est OK, mais sélection par Macro, Si au début du fichier on met:

                                          #define NO_STRONG 1

                                          Hop, il n'y en a pas, si on l'enlève la ligne, Hop elle revient (bien sur, il faut recompiler!).

                                          Mais j'ai pas trouvé pour la sélection par la définition du "template" (Mr Google est bien obscure, si on lui demande ce qu'est "C++ spécialisation template" !)

                                          Help! Trouve pas ! ... Snif ...

                                          (Source toujours au même endroit : https://github.com/Dedeun/ExoFraction.git)

                                          -
                                          Edité par Dedeun 29 septembre 2015 à 22:44:28

                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            29 septembre 2015 à 23:10:55

                                            J'hésite à te répondre... j'ai écrit un petit code compilation pour te montrer cela, mais cela voudrait dire que je te montre un code, sans donner plus d'explications. On est plus sur de la méta-programmation (basique) que de la programmation générique.

                                            Pour le moment, il est préférable que tu continues les exos comme cela. Le mieux ensuite est probablement que tu prennes un cours de C++ template, avant d'aborder cet exo.

                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              30 septembre 2015 à 0:25:15

                                              Sans passer par de la spécialisation, il y a toujours moyen de jouer avec la surcharge de fonction: (C'est même plus simple, la spécialisation partielle n'étant pas possible)

                                              //class nothrow_exception_guarantee {};
                                              class no_exception_guarantee {};
                                              //class basic_exception_guarantee {};
                                              class strong_exception_guarantee {};
                                              
                                              template<class T, class ExceptionGuarantee = no_exception_guarantee>
                                              class Fraction
                                              {
                                                //constexpr static bool is_noexcept = std::is_same_v<ExceptionGuarantee, nothrow_exception_guarantee>;
                                                ...
                                                Fraction& operator+=(Fraction const& f) //noexcept(is_noexcept)
                                                {
                                                  ....
                                                  plus_equal(ExceptionGuarantee{});
                                                  ....
                                                }
                                              
                                              private:
                                                void plus_equal(no_exception_guarantee) {
                                                  pas de garantie.
                                                }
                                              
                                                void plus_equal(strong_exception_guarantee) {
                                                  garantie forte.
                                                }
                                              }

                                              Mon dernier article de blog présente une autre approche (static_if).

                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                17 octobre 2015 à 15:00:20

                                                gbdivers a écrit:

                                                Pour le moment, il est préférable que tu continues les exos comme cela. Le mieux ensuite est probablement que tu prennes un cours de C++ template, avant d'aborder cet exo.

                                                Bonjour,

                                                Me revoici, sur le forum après quelques temps (Déplacement pro + quelques autres chose ...).

                                                Je retrouve l'exo de GBDivers (2ème version) Fractions avec infini (inf) et pas-un-nombre (NaN, not-a-number).

                                                Sur github: https://github.com/Dedeun/ExoFraction2.git

                                                Si vous avez quelques instant à y consacrer, ... merci d'avance pour vos remarques.

                                                (PS: si vous avez idée d'où je peux trouver un "cours de C++ template", je suis preneur. merci)

                                                Edit : Houps! J'avais pas vu ta réponse Jo Link Noir, je vais essayer de comprendre! Merci.

                                                -
                                                Edité par Dedeun 17 octobre 2015 à 15:02:54

                                                • Partager sur Facebook
                                                • Partager sur Twitter

                                                [Exo de GBDivers] autre implémentation.

                                                × 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