Attendons les plus calés, mais pour moi si tu utilise try tu peux meme faire des trucs comme des acces hors tableau ça ne plante pas l’appli alors que le deuxieme exemple peux te planter toute l’appli
Sinon les exceptions, c'est pour dérouter un comportement que l'on sait ne pas pouvoir continuer parce des entrées externes ne sont pas compatibles avec ce que l'on voudrait en faire.
Si j'ai bien compris, l'exception, dans le cas où elle est générique, va indiquer qu'il y a eu une erreur. Si je fais un if, je dois savoir quelle erreur je cherche. C'est ça?
Vu la vérification que tu réalises, je te dirai bien de lire les 3 articles sur la programmation par contrat dans mon blog (signature).
Un log trace ce qu'il se passe à un moment donné, il n'influe pas (drastiquement) le déroulement flot d'exécution. C'est totalement orthogonal à ce que je résume en dessous. i.e. on peut l'utiliser en plus, ou pas, c'est notre choix.
Une exception interrompt le flot d'exécution. Elles sont à privilégier pour vérifier une situation externe (d'origine) sur laquelle nous n'avons pas de contrôle depuis le code.
Une assertion est là pour indiquer que si elle n'est pas remplie il y a alors une erreur de conception/programmation et qu'il n'est pas possible de continuer car on n'a pas prévu le code pour résister à la situation. En cas de supposition non remplie, selon le mode d'exécution, on plante le programme et on force le développeur à corriger son erreur, ou on continue car on pense qu'il n'y a plus lieu d'impacter les performances à vérifier dynamiquement toutes les situations impossibles.
La difficulté est de trouver la frontière entre les deux. Je creuse la question dans mes billets de blog.
Dans tous les cas, un log ne suffit pas à arrêter de continuer quand on sait que la situation est anormale.
Dans ton cas, on peut certes créer une log, cependant, il faut penser à propager l'exception, faute de quoi, elle est considérée comme traitée et la propagation de cette dernière est interrompue, ce qui n'est pas désirable.
De plus, même si l'intention est louable, elle possède un gros inconvénient: La prolifération des blocs try / catch peut sévèrement nuire à la lisibilité de ton code source. Donc: A utiliser avec parcimonie.
N'abuse pas de la programmation défensive. Une fonction doit faire le minimum et ce n'est pas à elle de vérifier que tout ce qui est passé en entrée est juste.
Si la valeur peut venir de l'utilisateur, c'est au moment où tu fais la saisie que tu dois faire la vérification. La fonction elle n'a que faire de savoir de où la valeur vient.
En revanche, une assertion peut-être bienvenue pour détecter les erreurs de programmation pendant le développement.
- Edité par markand 17 février 2020 à 13:22:19
git is great because Linus did it, mercurial is better because he didn't.
> Attendons les plus calés, mais pour moi si tu utilise try tu peux meme faire des trucs comme des acces hors tableau ça ne plante pas l’appli alors que le deuxieme exemple peux te planter toute l’appli
Je réagis juste sur ça. Un accès hors tableau est toujours un problème et cause d'erreur. Contrairement à certains langages, il n'y a pas magiquement d'exception dans un tel scénario, c'est au développeur de vérifier le dépassement et de lancer une exception. Donc, pour qu'une exception arrive, il faut déjà faire cette vérification. Et après vient tout le débat sur programmation défensive vs programmation par contrat expliqué dans les articles de @lmghs
Je parlais des hors tableau car pour moi le try catch sert surtt pendant le dev, mais c’est pe etre une simplification négative de ma part.
Non, la raison d'être des exception est prendre en charge les évènements sur lesquels tu n'as aucun contrôle. Par exemple une allocation dynamique qui foire, ou une connection à une base de données qui saute.
Le thème chapeau: "que faire en situation anormale de type le monde m'en veut?". Par "le monde m'en veut", il faut comprendre: toutes ces erreurs liées à l'environnement sur lesquelles on ne peut avoir aucun contrôle par programmation: fichier/BD incorrect(/e) ou corrompu(/e), disque plein, plus de mémoire, connexion internet coupée à cause d'une tempête alors que l'on communiquait par socket, utilisateur qui dit n'importe quoi comme "chaussette" quand on lui demande son niveau etc.
Bref, il y a des erreurs externes. Ces erreurs il faut les faire remonter jusqu'à un moment où l'on pourra faire quelque chose avec notre programme: proposer de recommencer, dire "désolé nous allons devoir interrompre la suite de nos programmes veuillez revenir plus tard". Bref, interrompre le flot d'exécution nominal et se mettre à un point neutre (il doit y avoir un terme officiel) relativement à ce que l'utilisateur voudra faire.
Comment on remonte?
1- en cascadant les "if (error) return errorcode". Ce genre de programme doit avoir environ un if toutes les deux lignes pour être correct (je passe le lien vers l'article d'Aaron Lahman traduit par Alexandre Laurent sur dvpz). Si on oublie de vérifier un retour possiblement anormal pour continuer de propager l'erreur jusqu'au point neutre, on pourra vite se retrouver avec un programme au comportement complètement incompréhensible car buggué. Et oui on peut oublier de propager une situation anormale car on n'est pas obligé de rattraper un résultat de fonction en C, Java, Python, C++... Oublier les if, c'est le pays magique où les erreurs n'existent pas, c'est celui qui est enseigné trop souvent en premier de peur de faire peur aux débutants (au lieu de choisir les bons constructs en C++ comme le RAII...)
Accessoirement, le code assembleur généré pour code correct avec un if toutes les deux lignes va être parasité de branchements pour remonter les possibles erreurs. Ce n'est bon ni pour la prédiction de branches, ni pour le cache d'instructions. À noter que ce code, correct, parasité par un if toutes les deux lignes; sera lourd et difficile à maintenir. Même avec des macros.
2- en lançant une exception qui remonte toute la pile jusqu'au point neutre. Si on oublie le catch, l'exception remonte. Possiblement c'est exactement ce que l'on veut. Le code à maintenir est simple. Les chemins nominaux sont rapides. Le code est correct grâce au RAII (dans d'autres langages ce sera grâce à finally, ou encore au try-with-ressources (Java), using (C#), with (Python)...).
Par contre si une erreur est détectée, propager une exception devient très cher.
3- Des aficionados du fonctionnel sont "en train" d'importer des monades (cf boost.outcome) en C++ pour essayer de pallier aux faiblesses d'oubli potentiel du modèle de propagation manuel, sans imposer les exceptions à ceux qui n'aiment pas ça parce que trop magique.
<subjectif on>Perso, je ne suis pas entièrement convaincu. On a toujours un if toutes les deux lignes, mais l'avantage est que l'on ne peut pas l'oublier sinon cela ne compilera pas. Et donc il y a aussi parasitage du cache. Autre point négatif mon goût, c'est l'absence de support par le langage qui nous oblige toujours à explicitement avoir un if (ou macro) toutes les deux lignes. Bref, il y a toujours cette extra verbosité, parfois étrange grâce aux macros "simplificatrices".</>
Comment choisir?
Personnellement, c'est exception par défaut. Mais si le nombre de niveaux à remonter est extrêmement court (0 ou 1), et/ou que les "erreurs" de contexte à gérer sont excessivement plausibles, je vais passer par une propagation avec des branches explicites.
Exemple?
Ici, dans un code de tic-tac-toe (de dimension quelconque), L'utilisateur peut choisir d'abandonner la partie https://github.com/LucHermitte/tictactoe/blob/master/tictactoe.cpp#L605, lors de sa saisie clavier et cette situation sera remontée tout en haut. Je te laisse l'exercice d'identifier les niveaux intermédiaires et comment le code aurait dû être modifié pour remonter proprement la volonté de mettre un terme à la partie jusqu'au main(). Certes, le choix a été fait d’arrêter le programme. Mais dans d'autres média que la console, on aurait proposé une autre partie. (je sais j'ai complètement dévoyé les exceptions ici, ceci dit j'ai interprété une perte du canal de comm avec le joueur comme une volonté d'arrêt, mais techniquement, on ne peut vraiment plus communiquer avec le joueur, ce qui est un problème de contexte majeur)
A noter, que toutes les allocations sont aussi susceptibles de lancer des exceptions. On ne voit pas leurs `throw`mais ils sont là à chaque new, à chaque création de std::string, std::vector...
> Par exemple, la fonction sqrt(x) ne prend que des paramètres numériques positifs x, et elle renvoie des nombres toujours positifs qui vérifient result = x².
result = sqrt(x) <==> result = √x <==> result² = x
Billet de blog très intéressant à lire en tout cas merci pour ton travail
Tu veux parler du premier message? Malheureusement, Avec le code montré, cela pourrait cacher des erreurs potentiellement très compliqués à trouver sans outillage
× 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.
git is great because Linus did it, mercurial is better because he didn't.
https://zestedesavoir.com/tutoriels/822/la-programmation-en-c-moderne/
https://zestedesavoir.com/tutoriels/822/la-programmation-en-c-moderne/