La programmation défensive est une forme de conception défensive destinée à garantir le fonctionnement continu d'un logiciel face à des circonstances imprévues. Les pratiques de programmation défensive sont souvent utilisées là où une haute disponibilité, la sûreté ou la sécurité sont nécessaires.
La programmation défensive est une approche visant à améliorer le logiciel et le code source en termes de :
- Qualité générale – réduire le nombre de bugs et de problèmes logiciels.
- Compréhensibilité du code source – le code source devrait être lisible et compréhensible afin de passer un audit de code.
- Comportement prévisible du logiciel malgré des entrées inattendues ou des actions imprévues de l'utilisateur.
La manière habituelle d'adopter une approche de programmation défensive en C++ consiste à utiliser le mécanisme assert :
void test( int *p ) {
assert( p != 0 );
if (p == 0)
return;
// use p.
}Une assertion est du code utilisé pendant le développement — généralement une routine ou une macro — qui permet à un programme de se vérifier lui-même pendant son exécution. Lorsqu'une assertion est vraie, cela signifie que tout fonctionne comme prévu. Lorsqu'elle est fausse, cela signifie qu'elle a détecté une erreur inattendue dans le code.
Les assertions sont particulièrement utiles dans les grands programmes complexes et dans les programmes à haute fiabilité. Elles permettent aux programmeurs de débusquer plus rapidement les hypothèses d'interface incohérentes, les erreurs qui s'insinuent lorsque le code est modifié, etc.
Une assertion prend généralement deux arguments : une expression booléenne qui décrit l'hypothèse censée être vraie et un message à afficher si elle ne l'est pas.
Quels mécanismes les nouvelles normes C++ offrent-elles pour la programmation défensive ?
C++11 et static_assert
C++11 introduit une nouvelle façon de tester des assertions à la compilation grâce au nouveau mot-clé static_assert. Cette fonctionnalité est très utile pour ajouter des conditions aux paramètres de templates, comme le montre cette classe template issue du code source de Folly :

C++17 et l'attribut [[nodiscard]]
Les fonctions déclarées avec [[nodiscard]] ne devraient pas voir leur valeur de retour ignorée par l'appelant. Cela peut être utile si vous voulez vous assurer que les appelants vérifient une valeur de retour : vous pouvez faire respecter le contrat de code d'une fonction afin que l'appelant ne passe pas à côté de la valeur renvoyée.
Par exemple, si la valeur de retour de la fonction do_something n'est pas utilisée, le compilateur émettra un avertissement.
[[nodiscard]] error do_something (thing&);
do_something(my_thing); // Warning: ignored return valueC++20 et les contrats
C'est une amélioration majeure pour la programmation défensive dans les nouvelles normes : elle fournira de nombreuses facilités et fonctionnalités pour la conception par contrat.
Vous pouvez découvrir la proposition ici.
Examinons d'abord la terminologie utilisée dans la proposition de fonctionnalité des contrats :
1. A précondition is a predicate that should hold upon entry into a function. It expresses a function's expectation on its arguments and/or the state of objects that may be used by the function. Preconditions are expressed by expectsattributes (7.6.10).
2. A postcondition is a predicate that should hold upon exit from a function. It expresses the conditions that a function should ensure for the return value and/or the state of objects that may be used by the function. Postconditions are expressed by ensures attributes (7.6.11).
3. An assertion is a predicate that should hold at its point in a function body. It expresses the conditions, on objects that accessible at its point in a body, that must be satisfied. Assertions are expressed by assert attributes (7.6.12).
4. Preconditions, postoconditions, and assertions are collectively called contrats. A contract shall have no observable effect in a correct program (a program where all contracts would be satisified, if they were evaluated).
5. Contract attributes are followed by a expression-conditionnelle, which is a potentially evaluated expression (3.2).
Et voici un exemple d'utilisation de cette nouvelle fonctionnalité :
void push(int x, queue & q) [[expects: !q.full()]] [[ensures: !q.empty()]] { //... [[assert: q.is_valid()]]; //... }La norme C++20 apportera les fonctionnalités qui manquaient aux praticiens de la programmation défensive, éliminant le besoin de recourir à une bibliothèque ou un framework externe pour la conception par contrat.
