This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
in204:tds:sujets:td6:part1 [2020/11/01 10:48] bmonsuez |
in204:tds:sujets:td6:part1 [2022/11/18 10:49] (current) |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Partie I – Manipulation des Exceptions====== | + | ====== Partie I – Définitions des exigences ====== |
[[in204:tds:sujets:td6|TD6]] | [[in204:tds:sujets:td6|TD6]] | ||
- | ===== Question n°1 : Gestion simple des exceptions en C++ ===== | ||
- | ==== Question n°1.1 ==== | + | ===== Question n°1===== |
- | Implanter le code suivant dans un nouveau projet. | + | Créer un projet dans lequel vous intégrez le fichier "simple_sort.hpp" qui contient la fonction de comparaison suivante: |
<code cpp> | <code cpp> | ||
+ | #pragma once | ||
+ | #include<algorithm> | ||
- | #include <iostream> | + | template<typename iterator> |
- | + | void simple_sort(iterator start, iterator end) | |
- | double divide(double a, double b); | + | |
- | void test_divide(); | + | |
- | + | ||
- | void test_divide() | + | |
{ | { | ||
- | double i, j; | + | for(;start != end; start ++) |
- | for(;;) { | + | { |
- | std::cout << "Le numerateur (0 pour arreter): "; | + | auto it = start; it++; |
- | std::cin >> i; | + | for(;it != end; it ++) |
- | if(i == 0) | + | { |
- | break; | + | // Compare si les deux elements sont dans le bon ordre. |
- | std::cout << " Le denominateur : "; | + | if (*start > *it) |
- | std::cin >> j; | + | std::swap(*start, *it); |
- | std::cout << "Resultat: " << divide(i,j) << std::endl; | + | } |
- | } | + | } |
} | } | ||
+ | </code> | ||
- | double divide(double a, double b) | ||
- | { | ||
- | try { | ||
- | if(!b) throw b; | ||
- | } | ||
- | catch (double b) { | ||
- | std::cout << "Ne peut pas diviser par zero.\n"; | ||
- | return b; | ||
- | } | ||
- | return a/b; | ||
- | } | ||
- | |||
- | void main() | ||
- | { | ||
- | test_divide() ; | ||
- | } | ||
- | |||
- | </code> | ||
- | ==== Question n°1.2 ==== | + | ==== Question n°1.1 ===== |
- | Procéder à une exécution et regarder ce qui se passe quand une division par 0 se produit. | + | Déterminer quel type d'itérateur est requis pour effectuer les opérations ? |
<hidden Correction> | <hidden Correction> | ||
+ | Il s'agit d'un itérateur: | ||
+ | |||
+ | * devant offrir une itération croissante, | ||
+ | * devant offrir la possibilité de reprendre l'itération à partir d'une position antérieurement stockée. | ||
- | Lors d'une division par zéro se produit, le code | + | Si nous regardons les caractéristiques des itérateurs: [[https://en.cppreference.com/w/cpp/iterator|Iterator library]], nous constatons que: |
- | <code cpp> | + | |
- | std::cout << "Ne peut pas diviser par zero.\n"; | + | * //LegacyInputIterator//: ne supporte pas plusieurs passes et ne supporte pas l'écriture, |
- | return b; | + | * //LegacyForwardIterator//: supporte plusieurs passes mais ne supporte pas obligatoire l'écriture, |
- | </code> | + | * //LegacyOuputIterator//: supporte l'écriture. |
- | est exécuté et la fonction retourne ''0''. Ceci signifie que l'exception de type ''double'' ayant pour valeur ''0'' a été générée et a été capturée par la clause ''catch(double)''. | + | |
- | <code> | + | De fait, nous devons garantir à la fois la lecture et l'écriture, ce qui signifie que nous devons nous assurer que l'itérateur respect les contracts requis pour un //LegacyForwardIterator// et un //LegacyOuputIterator//. |
- | try { | + | |
- | if(!b) throw b; | + | |
- | } | + | |
- | catch (double b) { | + | |
- | std::cout << "Ne peut pas diviser par zero.\n"; | + | |
- | return b; | + | |
- | } | + | |
- | </code> | + | |
- | + | ||
- | Dans les autres cas, c'est bien le résultat de la division qui est retournée par la fonction. | + | |
</hidden> | </hidden> | ||
- | ==== Question n°1.3 ==== | ||
- | Exécuter en mode débogage et placer un point d’arrêt sur le code de capture de l’exception. | ||
- | <hidden Correction> | + | ==== Question n°1.2 ===== |
- | L'objectif de cette question est de vous faire manipuler vos outils pour utiliser l'environnement de dévogage de votre environnement de développement. Bien entendu, en fonction de votre environnement, vous avez des procédures différentes pour positionner un point d'arrêt. | + | |
- | Pour mémoire, voici quelques références pour placer des points d'arrêts sur les différents outils (sans garantie aucune d'exhaustivité). | + | Ajouter une contrainte imposant que le type passé à la fonction correspond bien au type d'itérateur que vous avez identifié. |
- | * [[https://code.visualstudio.com/docs/cpp/cpp-debug|Debug C++ in Visual Studio Code]] | + | <hidden Correction> |
- | + | <code cpp> | |
- | * [[http://wiki.codeblocks.org/index.php/Debugging_with_Code::Blocks|Debugging with Code::Blocks]] | + | template<typename iterator> |
- | + | void simple_sort(iterator start, iterator end) | |
- | * [[https://docs.microsoft.com/fr-fr/visualstudio/debugger/quickstart-debug-with-cplusplus?view=vs-2019|Quickstart: Debug with C++ using the Visual Studio debugger]] | + | requires(std::forward_iterator<iterator> && std::input_or_output_iterator<iterator>) |
- | + | { | |
- | * [[https://medium.com/yay-its-erica/xcode-debugging-with-breakpoints-for-beginners-5b0d0a39d711|Xcode Debugging with Breakpoints (for Beginners)]] | + | std::cout << "Insertion Sort\n"; |
- | + | for(;start != end; start ++) | |
- | * [[https://www.cs.swarthmore.edu/~newhall/unixhelp/howto_gdb.php|gdb (and ddd) Guide]] | + | { |
- | + | auto it = start; it++; | |
- | * [[https://www.emacswiki.org/emacs/DebuggingWithEmacs|Debugging with Emacs]] | + | for(;it != end; it ++) |
+ | { | ||
+ | // Compare si les deux elements sont dans le bon ordre. | ||
+ | if (*start > *it) | ||
+ | std::swap(*start, *it); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
</hidden> | </hidden> | ||
- | ===== Question n°2 : Création d’une classe d’exception ===== | ||
- | Nous envisageons désormais faire les choses correctement. Nous souhaitons définir une classe exception qui dérive de la classe [[https://en.cppreference.com/w/cpp/error/exception|std::exception]] se trouvant définie dans le fichier d'entête [[https://en.cppreference.com/w/cpp/header/exception|<exception>]]. Plus spécifiquement, nous souhaitons la faire dériver de la classe [[https://en.cppreference.com/w/cpp/error/runtime_error|std::runtime_error]] qui elle-même dérive de [[https://en.cppreference.com/w/cpp/error/exception|std::exception]]. | ||
- | Cette classe devra s’appeler ''division_by_zero''. | + | ==== Question n°1.3 ==== |
- | ==== Question n°2.1 ==== | + | Nous souhaitons ajouter l'opérateur suivant qui liste l'ensemble des éléments d'un conteneur: |
- | Créer la classe ''division_by_zero''. Elle pourra être définie dans un fichier d’entête ''math.hpp'' qui contiendra aussi l’entête de la fonction ''divide''. Le fichier associé ''math.cpp'' contiendre la code de la fonction divide. | + | <code cpp> |
+ | template<typename containerT, typename charT, typename traits = std::char_traits<charT>> | ||
+ | std::basic_ostream<charT, traits>& operator << (std::basic_ostream<charT, traits>& aStream, const containerT& aContainer) | ||
+ | { | ||
+ | aStream << "{"; | ||
+ | auto end = aContainer.end(); | ||
+ | for(auto it = aContainer.begin(); it != end;) | ||
+ | { | ||
+ | aStream << *it ++; | ||
+ | if(it != end) | ||
+ | aStream << ", "; | ||
+ | } | ||
+ | aStream << "}"; | ||
+ | } | ||
+ | </code> | ||
- | Penser à fournir un message d’erreur cohérent. | + | Ajouter ce code à votre fichier 'simple_sort.hpp' et puis tester ensuite à la fois la précédente fonction et cette nouvelle fonction avec le code suivant : |
- | + | ||
- | <hidden Correction> | + | |
- | + | ||
- | Nous nous proposons de créer un fichier ''math.hpp'' qui a le contenu suivant : | + | |
<code cpp> | <code cpp> | ||
- | #ifndef mathHPP | ||
- | #define mathHPP | ||
- | #include<exception> | + | #include<list> |
- | #include<stdexcept> | + | #include"simple_sort.hpp" |
- | class division_by_zero: public std::runtime_error | + | int main() |
{ | { | ||
- | public: | + | std::list<int> v = {1, 7, 3, 4, 9, 2, 5}; |
- | division_by_zero(): std::runtime_error("Division by zero") | + | simple_sort(v.begin(), v.end()); |
- | {} | + | std::cout << v; |
- | }; | + | return 0; |
- | + | } | |
- | double divide(double a, double b); | + | |
- | #endif | + | |
</code> | </code> | ||
- | Ainsi que le fichier ''math.cpp'' qui contient le code de la fonction ''divide''. | + | |
+ | ==== Question n°1.4 ==== | ||
+ | |||
+ | Dans la question précédente, nous avons défini une nouvelle surcharge de l'opérateur %%<<%%. En fait, cette surcharge n'est pas optimale, puisqu'il suffit que je définisse un type quelconque non supporté et cela ne fonctionne plus : | ||
<code cpp> | <code cpp> | ||
- | #include "math.hpp" | + | #include<list> |
+ | #include"sort.hpp" | ||
- | double divide(double a, double b) | + | struct Toto {}; |
+ | int main() | ||
{ | { | ||
- | try { | + | std::list<int> v = {1, 7, 3, 4, 9, 2, 5}; |
- | if(!b) throw b; | + | simple_sort(v.begin(), v.end()); |
- | } | + | std::cout << v; |
- | catch (double b) { | + | std::cout << Toto() << std::endl; |
- | std::cout << "Ne peut pas diviser par zero.\n"; | + | return 0; |
- | return b; | + | |
- | } | + | |
- | return a/b; | + | |
} | } | ||
- | |||
</code> | </code> | ||
- | </hidden> | ||
- | ==== Question n°2.2 ==== | + | Expliquer pourquoi le compilateur génére une erreur ? |
- | + | ||
- | Modifier les fonctions ''divide'' et ''test_divide'' pour prendre ne plus lancer et capturer une exception de type ''double'' mais de type ''division_by_zero''. | + | |
<hidden Correction> | <hidden Correction> | ||
- | + | En fait, il n'y a pas de surcharge de l'opérateur %%<<%% pour le type `Toto`, en conséquence, le compilateur va rechercher dans les opérateurs qui n'ont pas de contraintes sur les types, vous vous souvenez qu'on essaye d'abord les opérateurs les plus généraux et ensuite les moins généraux. Il trouve donc dans l'espace de nom courant l'opérateur : | |
- | La fonction modifiée s'écrit dorénavant comme suit : | + | |
<code cpp> | <code cpp> | ||
- | inline double divide(double a, double b) | + | template<typename containerT, typename charT, typename traits = std::char_traits<charT>> |
- | { | + | std::basic_ostream<charT, traits>& operator << (std::basic_ostream<charT, traits>& aStream, const containerT& aContainer) {...} |
- | try { | + | |
- | if(b == 0) | + | |
- | throw division_by_zero(); | + | |
- | } | + | |
- | catch (division_by_zero) { | + | |
- | std::cout << "Ne peut pas diviser par zero.\n"; | + | |
- | return 0; | + | |
- | } | + | |
- | return a/b; | + | |
- | } | + | |
</code> | </code> | ||
+ | |||
+ | et ne sait pas que cet opérateur n'est définit que pour les objets qui sont des conteneurs au sens de la STL. En conséquence de quoi, il génère une erreur puisqu'il essaye d'instancier un code qui ne supporte pas des objets de type `Toto`. | ||
</hidden> | </hidden> | ||
+ | |||
+ | |||
+ | [[in204:tds:sujets:td6:part2|Partie 2]] | ||
+ | |||