web-dev-qa-db-fra.com

Quels sont les contrats (comme proposé pour C ++ 17)?

Je lisais sur les contrats en Réflexions sur C++ 17 par B. Stroustrup et j'ai assisté à une petite présentation pour en parler mais je ne suis pas sûr de les avoir vraiment compris.

J'ai donc quelques interrogations et s'il est possible de les illustrer avec quelques exemples:

  • Les contrats remplacent-ils mieux la assert() classique et doivent-ils être utilisés ensemble? Quels contrats sont vraiment mis en termes simples pour un développeur de logiciels?

  • Les contrats auraient-ils une incidence sur la façon dont nous gérons les exceptions? Si oui, comment utiliser les exceptions et les contrats?

  • L'utilisation de contrats impliquerait-elle des frais généraux au moment de l'exécution? Serons-nous autorisés à les désactiver sur le code de sortie?

De la proposition N4415 :

Un contrat de pré-condition de l'opérateur d'indexation d'une classe Vector pourrait être rédigé:
T& operator[](size_t i) [[expects: i < size()]];

De même, un contrat de post-condition sur un constructeur d'une classe ArrayView pourrait être exprimé comme: ArrayView(const vector<T>& v) [[ensures: data() == v.data()]];

Merci au commentaire de @Keith Thompson:

Les contrats ne sont pas parvenus en C++ 20 . n nouveau groupe d'étude, SG21, a été créé.

34
coincoin

Pour autant que j'ai lu ce document: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4415.pdf

Les contrats font ce que assert essaie de faire de manière primitive depuis des années. Ils sont à la fois de la documentation et une affirmation au moment de l'exécution de la façon dont l'appelant doit appeler la fonction et dans quel état l'appelant peut-il s'attendre à ce que le code soit après le retour de la fonction. Celles-ci sont généralement appelées pré-conditions et post-conditions, ou invariants.

Cela aide à nettoyer le code du côté de l'implémentation, car avec les contrats, nous pouvons supposer qu'une fois l'exécution passée dans votre fonction, vos arguments sont dans un état valide (ce que vous attendez d'eux).

La partie post-conditions peut changer la façon dont vous gérez les exceptions, car avec les contrats, vous devrez vous assurer que lever une exception ne cassera pas vos post-conditions. Cela signifie généralement que votre code doit être protégé contre les exceptions, bien que cela signifie une garantie d'exception forte ou une garantie de base dépend de vos conditions.

Exemple:

class Data; 
 class MyVector {
 public: 
 void MyVector :: Push_back (Elem e) [[assure: data! = nullptr]] 
 {
 si (taille> = capacité) 
 {
 Données * p = données; 
 données = nullptr; // Juste pour les besoins de l'exemple ... 
 Data = new Data [capacité * 2]; // Pourrait lever une exception 
 // Copier p dans les données et supprimer p 
} 
 // Ajouter l'élément à la fin 
} 
 privé: 
 Données * données; 
 // autres données 
};

Dans cet exemple ici, si le constructeur de new ou Data lève une exception, votre post-condition est violée. Cela signifie que vous devez modifier tout ce code pour vous assurer que votre contrat n'est jamais violé!

Bien sûr, tout comme assert, les contrats peuvent inclure une surcharge d'exécution. La différence est que, puisque les contrats peuvent être mis dans le cadre de la déclaration de la fonction, le compilateur peut faire de meilleures optimisations, telles que l'évaluation des conditions sur le site de l'appelant ou même les évaluer au moment de la compilation. La section 1.5 du document mentionné au début de cet article parle des possibilités de désactiver les contrats en fonction de la configuration de votre build, tout comme les anciennes affirmations.

24
KABoissonneault

J'ai commencé avec le lien du document original OP fourni. Il y a des réponses, je suppose. Je recommande fortement de commencer par ce document. Voici la version TL&DR:

Les contrats ne sont pas un mécanisme général de rapport d'erreurs et ne remplacent pas les cadres de test. Au contraire, ils offrent une mesure d'atténuation de base lorsqu'un programme échoue en raison de l'inadéquation des attentes entre les parties d'un programme. Les contrats sont conceptuellement plus comme assert structuré () intégré dans la langue, jouant par les règles de sémantique de langue - donc la base pour l'analyse de programme et l'outillage de principe.

A propos de vos questions:

  • C'est assert () structuré, donc oui, on peut dire que dans certains cas assert () doit être remplacé par contract.
  • Permettez-moi d'utiliser une autre citation ici:

... l'expression d'un contrat doit logiquement faire partie de la déclaration de l'opération.

et exemple:

T& operator[](size_t i) [[expects: i < size()]];

À mon avis, c'est juste agréable et lisible.

  • Dans certains cas, les contrats peuvent remplacer les exceptions:

Cependant, c'est un critère de conception essentiel que les contrats soient utilisables dans des systèmes embarqués ou d'autres systèmes limités en ressources qui ne peuvent pas se permettre d'exceptions.

Dans les contrats de conditions préalables, des exceptions peuvent toujours être utilisées, car un comportement ultérieur après l'échec du contrat de conditions préalables n'est pas garanti.

  • Les frais généraux peuvent être réduits en activant/désactivant la validation des contrats dans les cas: utilisez tout, utilisez non, pré-condition uniquement, post-condition uniquement. Les contrats activés ajouteront certainement des frais généraux, comme tout type de chèques.

Quelques cas d'utilisation (comme je peux le supposer alors que je ne suis même pas près de développer une conception de contrats)

  1. Contrats - en cas de assert() habituelle car les contrats sont plus lisibles et peuvent être optimisés au moment de la compilation.
  2. Assert - dans les tests unitaires, les cadres de test, etc.
  3. Exceptions - peut être utilisé avec des contrats pré-conditionnés comme mentionné dans l'article:

La condition préalable d'une opération est évaluée avant toute autre instruction dans le corps de la fonction. Si le résultat est vrai, le contrôle normal de l'exécution continue jusqu'à la première instruction dans le corps de la fonction. Sinon, une exécution ultérieure n'est pas garantie: soit le programme s'interrompt, soit lève une exception, ou s'il est autorisé à continuer, le comportement n'est pas défini.

Il y a aussi certainsautres suggestions sur la mise en œuvre des contrats, donc notre enquête est juste prématurée.

9
Pavel Oganesyan

Il n'est pas facile de répondre à vos questions autrement qu'avec: Cela dépend. En effet, on ne sait pas encore exactement quels contrats vont être exactement. Il existe actuellement plusieurs propositions et idées:

  • n4378 Lakos et al. propose essentiellement de normaliser une boîte à outils d'assert sophistiquée. Les contrats sont vérifiés à l'intérieur d'une implémentation de fonctions, 3 niveaux d'assertions différents sont fournis pour contrôler le nombre de vérifications d'exécution et le traitement des violations d'assertions peut être personnalisé.

  • n4415 dos Reis et al. et n4435 Brown sont assez similaires et proposent une syntaxe basée sur les attributs pour définir les conditions de pré et post dans les interfaces de fonctions. Ils n'entrent pas dans les détails sur le contrôle qu'ils accordent aux contrôles d'exécution et sur le comportement des violations.

Il existe également des articles moins récents sur ce sujet. Il existe de nombreux détails qui ne sont pas encore décidés, et cette fonctionnalité touche de nombreux domaines différents (par exemple, modules, optimisation, création/liaison) sur certains d'entre eux, la norme a peu de contrôle.

Votre question sur les exceptions est particulièrement difficile, car les interactions entre la gestion des violations de contrat et les exceptions ne sont pas claires (par exemple, un gestionnaire de violation de contrat pourrait-il lancer (utile dans les cadres de test)? Que faire si la fonction est noexcept(true)?).

3
Fabio Fracassi