Assertions illogiques
Par Emmanuel Deloget, vendredi 2 février 2007 à 10:00 :: Expériences :: permalien #48
Tags: C++, code dangereux, fun
Les assertions sont un mécanisme puissant et permettent de contrôler les valeurs des données, de manière à effectuer une vérification dynamique de leur cohérence. Elles sont toutefois difficiles à utiliser - de par leur nature - et de fait, se retrouvent souvent être utilisées d’une mauvaise manière.
Considérez le code suivant, récupéré du projet sur lequel je travaille (et violemment modifié lors de ce processus, pour des raisons évidentes) :
void do_something(std::vector<some_type>& container)
{
T* first_match = NULL;
T* last_match = NULL;
for (iterator i=container.begin() ; i != container.end(); ++i) {
if (validate_data(*i)) {
if (!first_match) {
first_match = last_match = &(*i);
continue;
}
assert(last_match != NULL);
if (&(*i) < first_match) {
first_match = &(*i);
} else if (&(*i) > *last_match) {
last_match = &(*i);
}
}
}
assert(first_match < last_match);
do_something_with(first_match, last_match);
}
Ce code contient deux assertions – et aucune d’elle n’est valide.
La première assertion n’est pas valide pour la simple et bonne raison qu’elle ne peut jamais être fausse. La seule manière pour last_match d’être NULL à ce point est d’avoir first_match lui aussi égal à NULL – mais dans ce cas, on entre dans le test, et last_match est initialisé à une valeur non nulle. Cette assertion est donc complètement illogique, et ne sert à rien.
La seconde considère qu’on a toujours first_match < last_match en sortir de boucle. Si aucune donnée n’a été validée par validate_data(), ou si une seule donnée l’a été, cette assertion est fausse (car first_match == last_match). Remplacer l’assertion par assert(first_match <= last_match) n’apporte rien, car NULL <= NULL.
En fait, cette seconde assertion est invalide pour une raison simple : il ne s’agit pas d’une assertion, mais d’un contrôle des valeurs effectué afin de détecter de possibles erreurs. Si first_match (un pointeur) doit être inférieur à last_match (un autre pointeur) au sortir de la boucle, alors le fait de ne pas avoir ce résultat ne signifie pas qu’on a fait une erreur de programmation, mais que les données sont probablement mauvaises ou corrompues. En tout état de cause, il s’agit d’une erreur qui doit être détectée à tout coup, et pas seulement dans le cas ou les assertions sont actives.
Il y a des moments où il faut se poser des questions lorsqu’on manipule des assertions.
Commentaires
Aucun commentaire pour le moment.
:: Fil rss des commentaires de ce billet ::
Ajouter un commentaire