Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Exercice POO] wrapper C++ pour le typage fort

    6 août 2017 à 19:44:44

    Contexte

    Vous travaillez sur une bibliothèques de calculs mathématiques et on demande à votre équipe d'écrire des fonctions permettant de calculer les racines n-ièmes d'un nombre donné. Le code de tests sera similaire au code suivant :

    #include <iostream>
    
    int main() {
        float input { 0 };
        std::cin >> input;
    
        std::cout << racine2(input) << std::endl;
        std::cout << racine3(input) << std::endl;
        std::cout << racine4(input) << std::endl;
        std::cout << racine6(input) << std::endl;
    }

    Ces fonctions "racine" n'accepte pas de valeurs négatives. Votre travail consiste à garantir que les fonctions sont correctement utilisées, en écrivant le code permettant de vérifier les contraintes sur les valeurs.

    Vous n'avez pas besoin d'écrire le code permettant de réaliser les calculs, ce travail est réalisé par une autre personne de votre équipe. (Considérez que vous pouvez utiliser des fonctions "racine2_internal", "racine3_internal", etc)

    Vous pouvez également changer le code d'appel des fonctions (dans la fonction "main"), la signature des fonctions "racine" ou tout ce que vous estimer nécessaire pour que le code soit valide.

    Etape 1 : vérification des donnees en entree

    Quand on regarde le code de la fonction "main", on peut la décomposer en 2 parties :

    • la partie ou la valeur peut etre positive ou négative ;
    • la partie du code ou la valeur DOIT etre positive.
    En testant les valeurs a cet endroit, on a la garantie que les fonctions "racine" sont utilisées correctement.
    #include <iostream>
    
    int main() {
        float input { 0 };
        std::cin >> input;
    
        // verification des entrees ici
    
        std::cout << racine2(input) << std::endl;
        std::cout << racine3(input) << std::endl;
        std::cout << racine4(input) << std::endl;
        std::cout << racine6(input) << std::endl;
    }

    Question 1 : écrire le code de vérification, en utilisation une assertion.

    Question 2 : écrire le code de vérification, en utilisation un test "if".

    Question 3 : quelle est la différence entre utiliser "assert" ou "if" ? Lequel est le mieux, dans ce cas ? 

    Etape 2 : validation des paramètres d'entrée des fonctions

    Votre bibliothèque mathématique ne sera pas utilisées uniquement dans cette fonction "main", vérifier les données entrées dans le code appelant n'est pas suffisant, il faut aussi vérifier dans votre code. Le code sera similaire au suivant :

    float racine2(float input) 
    {
        // verification des donnees
    
        return racine2_internal(input);
    }

    Question 4 : écrire le code de vérification, en utilisation une assertion.

    Question 5 : écrire le code de vérification, en utilisation un test "if".

    Pour simplifier le code, votre équipe a décidé que les fonctions "racine4" et "racine6" seront implémentées en utilisant les fonctions "racine2" et "racine3".

    float racine4(float input) 
    {
        return racine2(racine2(input));
    }
    
    float racine6(float input) 
    {
        return racine2(racine3(input));
    }

    Question 6 : quelle est la différence entre utiliser "assert" ou "if" ? Lequel est le mieux, dans ce cas ? 

    Etape 3 : utilisation du typage fort

    Le problème avec les approches précédentes, c'est que la validation des valeurs est faite à l'exécution. Donc cela peut avoir un coût (à l'exécution, à la maintenance du code, à l'écriture des tests). Ce qu'il faudrait, c'est un type équivalent a "float", mais qui accepterait uniquement des valeurs positives. Il serait alors possible d'écrire un code proche de :

    PositiveFloat racine2(PositiveFloat input) 
    {
        return racine2_internal(input);
    }

    On vous demande d'écrire cette classe "PositiveFloat".

    class PositiveFloat 
    {
    public:
        ...
    
    private:
        float m_value { 0.0f };
    };

    Question 7 : est-ce que cette classe doit avoir une sémantique de valeur ou d'entité ?

    Question 8 : faut-il écrire un constructeur par défaut et un destructeur ? Si oui, écrivez le.

    Question 9 : faut-il écrire les constructeurs et opérateur de copie et de déplacement ? Si oui, écrivez les. (Question optionnelle : expliquez ce qu'est la forme canonique orthodoxe de Coplien et les règles "des 5", "des 3" et "du 0").

    Question 10 : écrire un constructeur prenant en paramètre un "float". 

    Question 11 : écrire un opérateur de conversion en "float".

    Question 12 : écrire les opérateurs de comparaison. (Les 6)

    Question 13 : écrire les opérateurs arithmétiques, sans vérification de dépassement des calculs. (Les 8)

    Question 14 (optionnelle) : écrire les opérateurs arithmétiques avec vérification de dépassement des calculs.

    Question 15 (optionnelle) : écrire une version template de cette classe, pour supporter d'autres types que "float".

    Cette approche peut etre étendue à d'autres contraintes sur les types. Par exemple, pour une classe représentant un nombre, vérifier que le nombre n'est pas nul ("BoundedFloat") ou qu'il est compris entre min et max ("RangedFloat"). Mais également sur autre chose que des nombres : vérifier qu'un pointeur n'est pas null ("NonNull"), qu'une paire d'itérations proviennent du même conteneur ("range"), qu'une collection ne contient pas d'élément en double ("Unique") ou est trié ("Sorted"), etc.

    Question 16 et suivantes (optionnelle) : écrire toutes ces classes :)

    -
    Edité par gbdivers 6 août 2017 à 22:40:13

    • Partager sur Facebook
    • Partager sur Twitter
      8 août 2017 à 5:00:28

      Salut gbdivers,

      j'ai commencé a lire ton cours de C++ quand j'ai lu sur un blog toutes les parties qu'openclassroom zappe complètement et les anneries qu'il apprend.

      avouons que ton exercic est impossible avec la seule lecture du cours d'openclassroom ;-).

      Ton projet de rédiger un cours complet du c++ est louable, j'adorerai y contribuer d'une manière ou d'une autre, mais compliqué vu mes connaissances actuelle du C++ (2 ans de c++ en amateur sur des cours en ligne, je suis en terminale j'attend mon dut info avec impatience ;-) ). Les syntaxes vues dans ton cours et les notions qu'il aborde sont très intéressante.

      Je finit ton cours et fait de suite ton exercice ;-)

      • Partager sur Facebook
      • Partager sur Twitter
      un projet ? Fait le ou ne le fait pas, il n'y a pas d'essai.
        11 août 2017 à 14:05:10

        J'aime beaucoup :)

        Le premier point de la Q8 a des réponses non triviales. Spoiler en filptext.org: ˙"ǝnןɐʌ pǝzıןɐıʇıuıun pɐǝɹ" ǝdʎʇ ǝp ǝnbıɯɐuʎp no ǝnbıʇɐʇs ǝsʎןɐuɐ,ן ǝp à éqéq ǝן ɹǝןıɟǝɹ no ¿ɹǝsıןɐıʇıuı-oɹéz ןı-ʇnɐɟ

        Pour les BoundedFloat & cie, pour m'y être essayé la dernière fois, c'est compliqué de faire un truc simple à utiliser.

        • Partager sur Facebook
        • Partager sur Twitter
        C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.

        [Exercice POO] wrapper C++ pour le typage fort

        × 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