Magazine

Bien utiliser assert()

Publié le 24 juin 2008 par Carlseleborg
Début 2006, j'ai écrit un article intitulé Asserter n'est pas jouer, qui traitait des assertions en C++. Avec deux ans d'expérience en plus dans les pattes, je me rends compte qu'il est grand temps de peaufiner, voire de corriger mon avis sur la question. C'est d'autant plus utile que peu de développeurs savent vraiment utiliser les assertions. En effet, la gestion des erreurs fait partie, avec la programmation concurrente, de ces domaines de la programmation dont la difficulté est largement sous-estimée.

A quoi servent les assertions?

La définition classique, du genre "les assertions permettent de détecter lorsqu'une condition qui doit être vraie n'est pas vérifiée", est à peu près aussi utile qu'un GPS dans un tunnel: elle nous dit ce que sont les assertions, mais pas comment les utiliser. Les assertions servent à une seule chose: vous taper sur les doigts lorsque vous ou l'un de vos collègues introduisez un bug dans le code. Ni plus, ni moins.
Si votre code suppose quelque chose d'une variable, assertez, et faites comme si tout allait bien. La pire chose à faire, c'est d'essayer de retomber sur ses pieds en corrigeant des valeurs non valides ou en gérant des conditions bizarres "au cas où": non seulement vous aurez caché le bug, mais vous l'aurez même rendu plus difficile encore à détecter. C'est mal !
Bug, bug, bug. Une fois que ceci est rentré dans le crâne, on comprend deux choses:
  • On ne peut pas s'en servir pour valider une condition influencée par une donnée venant "des autres".
  • Supprimer les assertions dans le code de production est une aberration.

C'est qui, les autres ?

Les autres, par rapport à moi, ce sont tous ceux qui ne sont pas dans mon équipe de développement. Il peut s'agir aussi bien de l'utilisateur final, de l'autre côté de l'écran ou de mon API, que d'un fournisseur d'une classe ou d'une fonction que j'utilise moi-même. Pour moi, tout ce qui ne vient pas du code produit par mon équipe est suspect, mais n'est pas de mon ressort. On ne peut donc pas utiliser les assertions pour :
  • valider un paramètre d'entrée si la personne susceptible d'utiliser la fonction n'a pas accès au code de celle-ci,
  • vérifier que l'utilisateur, ce cloporte mal éduqué, a bien entré un nombre entre 1 et 10, comme c'est marqué en gras avec du texte rouge qui clignote à deux pixels de la zone de saisie,
  • vérifier qu'un fichier, si important soit-il pour l'application, est bien présent et contient des données valides,
  • vérifier que malloc() renvoie bien un pointeur non nul (notez que dans Asserter n'est pas jouer, j'avais testé la valeur de retour de new, qui d'après le standard doit lancer une exception si l'allocation échoue, mais j'utilisais à l'époque Visual C++ 6.0 qui ne s'y conformait pas sur ce point-là)
  • répandre la paix sur terre
Par contre, pour toutes les conditions d'erreur où il est possible d'aller trouver le fautif dans un bureau avoisinant, l'assertion est le meilleur outil. Si en plus, en cas d'assertion qui échoue, vous en profitez pour lui verser du Tabasco dans son café, le respect pour les assertions montera en flèche dans votre équipe.

Pourquoi faut-il laisser les assertions dans le code de production ?

Par code de production, j'entends la version compilée en mode "release". Toutes les invocations de la macro assert() standard disparaissent comme par enchantement lorsque DEBUG n'est pas défini. Le raisonnement est qu'une fois le code bien testé, on peut se passer de ces vérifications et gagner ainsi en performance.
C'est très très mal !
C'est vrai, il faut du temps pour accepter que que son code ne sera jamais totalement, complètement testé. C'est cette fameuse humilité à laquelle Jeff Atwood fait référence dans son blog Coding Horror. Mais des bugs, nous en laisserons toujours derrière nous. Du coup, la question change: il ne s'agit plus de savoir quand l'application sera complètement débuggée, mais plutôt de savoir comment elle doit réagir en présence d'un bug. Les assertions servent justement à ça.
La bonne réponse, aussi désagréable soit-elle, c'est de tuer l'application le plus vite possible. C'est le rôle de l'assertion: après avoir détecté un bug, elle doit planter l'application en exécutant un minimum de code. La solution est dramatique, mais ne pas le faire peut être pire encore. Vous pouvez par exemple corrompre des données. C'est le pêché ultime dans le mode du logiciel, qui vous apporte malédiction à vous et votre descendance sur sept générations. Mieux vaut planter avec panache que de cacher une erreur.
Toujours pas convaincu? Imaginez que vous programmez un système médical de respiration assistée. Les petits systèmes embarqués comme ça ont souvent un mécanisme qui permet de détecter que l'application s'est terminée et la redémarrent aussi sec. Mettons qu'au bout de 24h de fonctionnement, l'une de vos assertions détecte une erreur dans la fonction qui détecte les seuils de débit pour le déclenchement. Vous ne voulez surtout pas asphyxier votre pauvre patient! En tuant le programme, votre assertion lui permettra de redémarrer dans un état sain.
Il n'est d'ailleurs pas souhaitable d'implémenter des assertions sous forme d'exception. Même si celle-ci remonte jusqu'au main(), elle déclenchera sur son chemin une flopée de destructions d'objets. Or votre programme à ce moment-là, il faut bien le dire, il est bien pourri, et même les destructeurs peuvent faire des bêtises! Moins on exécute de code, mieux c'est. Votre nouvelle meilleure amie: abort().

Apprendre de ses erreurs

Mais s'il faut terminer au plus vite, c'est en revanche dommage de manquer l'occasion d'avoir plus d'info sur le problème. En implémentant votre propre mécanisme d'assertions, vous pourrez rassembler toute l'information qu'il vous faut, empaqueter tout cela à votre guise et vous le renvoyer directement chez vous (ou si vous êtes très fort, vous le faire envoyer par e-mail par l'utilisateur). En développement, profitez-en pour faire intervenir le débuggeur. En production, soyez sympa, évitez à votre utilisateur la surprise de voir votre application disparaître subitement, et montrez-lui au moins un petit message en lui expliquant bien que c'est de votre faute.
Et si vous êtes le malheureux développeur du ventilateur mécanique, déclenchez donc une petite alarme, histoire qu'une infirmière puisse vérifier l'état du patient que vous venez de laisser tomber...

Retour à La Une de Logo Paperblog

A propos de l’auteur


Carlseleborg 1 partage Voir son profil
Voir son blog

l'auteur n'a pas encore renseigné son compte l'auteur n'a pas encore renseigné son compte