Namespaces
Variants

Contract assertions (since C++26)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Les assertions contractuelles permettent au programmeur de spécifier des propriétés de l'état du programme qui sont censées être vérifiées à certains points durant l'exécution.

Table des matières

Explication

Assertions contractuelles sont introduites par les spécificateurs de contrat de fonction et les instructions contract_assert . Chaque assertion contractuelle possède un prédicat  , qui est une expression de type bool .

Évaluation des assertions de contrat

L'évaluation d'une assertion contractuelle utilise l'une des sémantiques d'évaluation suivantes :

Sémantique d'évaluation Est une sémantique de vérification Est une sémantique de terminaison
ignorer
observer Oui
appliquer Oui Oui
appliquer-rapide Oui Oui

Il est défini par l'implémentation quelle sémantique d'évaluation est utilisée pour toute évaluation donnée d'une assertion de contrat. Les sémantiques d'évaluation peuvent différer pour différentes évaluations de la même assertion de contrat, y compris les évaluations durant l'évaluation constante.

Si la sémantique « ignore » est utilisée, l'évaluation d'une assertion de contrat n'a aucun effet.

Si une sémantique de vérification est utilisée, l'évaluation E d'une assertion contractuelle détermine la valeur du prédicat. Il n'est pas spécifié si le prédicat est évalué. Si l'une des conditions suivantes est satisfaite, une violation de contrat se produit :

Il existe un point de contrôle observable CP qui se produit avant E tel que toute autre opération OP qui se produit avant A se produit également avant CP .

int num = 0;
void f() pre((num++, false));
f(); // L'incrémentation de « num » pourrait ne pas se produire, même si une sémantique de vérification est utilisée

Gestion des violations de contrat

Si une violation de contrat se produit dans un contexte manifestement évalué de manière constante :

  • Si la sémantique d'évaluation est « observe », un diagnostic est produit.
  • Si la sémantique d'évaluation est une sémantique de terminaison, le programme est mal formé.

Si une violation de contrat survient dans un contexte qui n'est pas manifestement évalué de manière constante :

  • Si la sémantique d'évaluation est « quick-enforce », le programme est terminé par contrat.
  • Si la sémantique d'évaluation est « enforce » ou « observe », le gestionnaire de violation de contrat est invoqué avec une lvalue se référant à un objet obj de type const std :: contracts :: contract_violation contenant des informations sur la violation du contrat.
    • Le stockage pour obj est alloué de manière non spécifiée, mais aucune fonction d'allocation globale ne sera invoquée.
    • La durée de vie de obj persiste pendant la durée de l'invocation du gestionnaire de violation de contrat.

Programmes résiliés par contrat

Lorsque le programme est contract-terminated  , il est défini par l'implémentation (selon le contexte) si

Gestionnaire de violation de contrat

Le gestionnaire de violation de contrat d'un programme est une fonction nommée :: handle_contract_violation :

void handle_contract_violation ( std :: contracts :: contract_violation ) ;
(depuis C++26)
(optionnellement noexcept)

Une définition du gestionnaire de violation de contrat, appelé le gestionnaire de violation de contrat par défaut  , est fournie par l'implémentation (au lieu d'un en-tête de bibliothèque standard).

Il est défini par l'implémentation si le gestionnaire de violation de contrat est remplaçable . Si le gestionnaire de violation de contrat n'est pas remplaçable, une déclaration d'une fonction de remplacement pour le gestionnaire de violation de contrat est mal formée, aucun diagnostic requis.

Lorsque le gestionnaire de violation de contrat retourne normalement :

  • Si la sémantique d'évaluation est « observe », le flux de contrôle continue normalement après le point d'évaluation de l'assertion de contrat.
  • Si la sémantique d'évaluation est « enforce », le programme est terminé par contrat.

Il existe un point de contrôle observable CP qui se produit après que le gestionnaire de violation de contrat retourne normalement, de sorte que toute autre opération OP qui se produit après que le gestionnaire de violation de contrat retourne se produit également après CP .

Gestion des exceptions provenant des assertions

Si la violation du contrat s'est produite parce que l'évaluation du prédicat s'est terminée via une exception et que la sémantique d'évaluation est « observe » ou « enforce », le gestionnaire de violation de contrat est invoqué depuis un gestionnaire implicite actif pour cette exception.

Lorsque le gestionnaire de violation de contrat retourne normalement :

  • Si la sémantique d'évaluation est « observe », le gestionnaire implicite n'est plus considéré comme actif.
  • Si la sémantique d'évaluation est « enforce », le gestionnaire implicite reste actif lors de la terminaison du contrat.

L'exception actuelle peut être inspectée ou relancée dans le gestionnaire de violation de contrat en utilisant std::current_exception() .

Évaluer en séquence

Pour évaluer en séquence une liste R d'assertions de contrat :

1) Construire une liste d'assertions de contrat S telle que toutes les conditions suivantes soient satisfaites :
  • Tous les éléments de R sont dans S .
  • Chaque élément de R peut être répété un nombre de fois défini par l'implémentation dans S .
  • Si une assertion de contrat A précède une autre assertion de contrat B dans R , alors la première occurrence de A précède la première occurrence de B dans S .
2) Évaluez chaque élément de S de telle sorte que, si une assertion de contrat A précède une assertion de contrat B dans S , alors l'évaluation de A est séquencée avant l'évaluation de B .
void f(int i)
{
    contract_assert(i > 0);  // #1
    contract_assert(i < 10); // #2
    // séquence d'évaluations valide :   #1 #2       (pas de répétition)
    // séquence d'évaluations valide :   #1 #1 #2 #2 (répétition en séquence)
    // séquence d'évaluations valide :   #1 #2 #1 #2 (répétition alternative)
    // séquence d'évaluations valide :   #1 #2 #2 #1 (les secondes occurrences peuvent changer d'ordre)
    // séquence d'évaluations invalide : #2 #1       (les premières occurrences ne peuvent pas changer d'ordre)
}

Notes

La portée et la flexibilité des choix disponibles de sémantiques d'évaluation dépendent de l'implémentation, et ne doivent pas nécessairement permettre les quatre sémantiques d'évaluation comme possibilités.

Le choix de sémantiques d'évaluation différentes pour la même assertion de contrat dans différentes unités de traduction peut entraîner des violations de la règle de définition unique lorsqu'une assertion de contrat a des effets secondaires qui modifient la valeur produite par une expression constante :

constexpr int f(int i)
{
    contract_assert((++const_cast<int&>(i), true));
    return i;
}
inline void g()
{
    int a[f(1)]; // taille dépendante de la sémantique d'évaluation de contract_assert ci-dessus
}

Si la valeur qui résulterait de l'évaluation du prédicat est true , aucune violation de contrat ne se produit et le flux de contrôle continue normalement après le point d'évaluation de l'assertion de contrat.

Si l'évaluation du prédicat se termine par des sauts non locaux ou en terminant le programme, aucune violation de contrat ne se produit non plus.

Il est recommandé par la norme C++ que le gestionnaire par défaut de violation de contrat devrait produire une sortie de diagnostic qui formate de manière appropriée les contenus les plus pertinents de l'argument (avec limitation de débit pour les violations potentiellement répétées des assertions de contrat observées), puis retourne normalement.

Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_contracts 202502L (C++26) Contrats

Mots-clés

contract_assert , pre , post

État de la prise en charge

Fonctionnalité C++26

Document(s)

GCC
Clang
MSVC
Apple Clang
EDG eccp
Intel C++
Nvidia HPC C++ (ex PGI)*
Nvidia nvcc
Cray


Contracts ( FTM ) * P2900R14

Voir aussi

contract_assert statement (C++26) vérifie une condition interne durant l'exécution
spécificateurs de contrat de fonction (C++26) spécifie les préconditions ( pre ) et postconditions ( post )