IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Programmation par contrat en C++


Sujet :

C++

  1. #1
    Membre chevronné
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Par défaut Programmation par contrat en C++
    Bonjour,

    Petite question sur la mise en oeuvre de la programmation par contrat en C++. Dans la mesure où l'on n'a pas de syntaxe spécifique pour spécifier les contrats (comme des clauses requires en ensures par exemple), comment peut-on les spécifier autrement que par des commentaires ?

    Les asserts en début de méthodes pour vérifier les préconditions sont pour moi une forme déguisée de test sur les préconditions, chose que l'on n'est pas sensé faire.

    Les ruptures de contrat sont sensées lever des exceptions, comment le faire si on ne vérifie pas les conditions ?

    Merci d'avance.

  2. #2
    Expert confirmé

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 756
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 756
    Billets dans le blog
    3
    Par défaut
    J'ai entendu parler de techniques de ce genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    class Test
    {
    public:
        void DoSomething();
    private:
        virtual void DoSomethingImpl() = 0;
    };
     
    void Test::DoSomething()
    {
        // verification des preconditions
        this->DoSomethingImpl();
        // verification des post conditions
    }
    Les implémentations héritent de Test, et le client utilise uniquement les fonctions publiques non virtuelles de Test.

    On peut imaginer des solution a base de template wrapper...

  3. #3
    Membre chevronné
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Par défaut
    Oui mais il y a dans ce code les vérifications des conditions, chose qu'on est sensé éviter de faire.

    Le fait d'utiliser de wrapper les méthodes virtuelles par des non virtuelles permet juste d'isoler proprement l'interface de l'implémentation (on pourrait carrément aller plus loin en appliquand l'idiome Pimpl).

    Rajouter les vérifications des conditions dans les méthodes virtuelles contredit la règle selon laquelle une méthode n'est pas sensé tester sa précondition. A moins que cette règle soit basée sur la possibilité du langage de spécifier et vérifier les pré et post conditions de manière transparente. Dans ce cas on a pas d'autres choix en C++ que de tester ces conditions, mais je ne sais pas si on peut toujours parler de programmation par contrat du coup

  4. #4
    Membre émérite

    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    717
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 717
    Par défaut
    Oui mais il y a dans ce code les vérifications des conditions, chose qu'on est sensé éviter de faire.
    Je ne comprend pas ce que tu veux dire, le C++ ne supportant pas nativement la programmation par contrat, il faut forcement faire les vérifications "à la main" . Que ce soit les pré-conditions, les post-conditions ou les invariants, on les indiques par des commentaires dans le .h et on les vérifie avec des assert() ou équivalent dans l'implémentation.

    À noter que les exceptions ne sont pas une bonne façon de gérer les erreurs de contrat.

  5. #5
    Membre chevronné
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Par défaut
    Citation Envoyé par Sylvain Togni Voir le message
    Je ne comprend pas ce que tu veux dire, le C++ ne supportant pas nativement la programmation par contrat, il faut forcement faire les vérifications "à la main" . Que ce soit les pré-conditions, les post-conditions ou les invariants, on les indiques par des commentaires dans le .h et on les vérifie avec des assert() ou équivalent dans l'implémentation.
    Disons que les vérifications à la main me paraissent plutôt orientées programmation défensive, donc à l'opposé de ce que propose la programmation par contrat.

    À noter que les exceptions ne sont pas une bonne façon de gérer les erreurs de contrat.
    A cause de la perte de contexte ? Dans le cas d'une version DEBUG, l'assert passe, dans le cas d'une release comment signaler à l'appelant que le contrat est rompu autrement que par une exception ? Un arrêt complet de l'exécution n'est aps forcément acceptable.

  6. #6
    Membre émérite

    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    717
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 717
    Par défaut
    Disons que les vérifications à la main me paraissent plutôt orientées programmation défensive, donc à l'opposé de ce que propose la programmation par contrat.
    La programmation par contrat est un type de programmation défensive. Elles ne sont pas opposées.

    Dans le cas d'une version DEBUG, l'assert passe
    Une version DEBUG, comme le nom l'indique, c'est pour debugger. La programmation par contrat, c'est pour trouver plus facilement les bugs. Il me semble donc normal que les deux soient liés.

    dans le cas d'une release comment signaler à l'appelant que le contrat est rompu autrement que par une exception ?
    L'appelant n'a pas à savoir si le contrat est rompu, il doit respecter le contrat. S'il ne le respecte pas, c'est un bug, et un arrêt complet est une solution.

  7. #7
    Membre chevronné
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Par défaut
    Citation Envoyé par Sylvain Togni Voir le message
    La programmation par contrat est un type de programmation défensive. Elles ne sont pas opposées.
    Je pensais que la programmation défensive était le fait de blinder son code en vérifiant au maximum les arguments passés à une méthode pour traiter un maximum de cas possibles, donc plus vraiment besoin de contrat. D'où justement mon interrogation sur sa mise en oeuvre en C++ ; les notions de programmation par contrat que j'ai viennent du bouquin de Meyer où il oppose justement programmation défensive et programmation par contrat, maintenant peut être que cela est dû au fait que le langage utilisé permet une spécification du contrat dans la déclaration des méthodes.


    L'appelant n'a pas à savoir si le contrat est rompu, il doit respecter le contrat. S'il ne le respecte pas, c'est un bug, et un arrêt complet est une solution.
    Si je livre un framework à un client et qu'un appel au framework dans son code échoue, il souhaitera sûrement savoir quelle condition n'est pas respectée (pré ou post) pour savoir où se trouve le bug justement : dans son code, ou dans celui du framework. Il faut donc bien un moyen lui spécifier que le contrat est rompu et à quel endroit non?

    Autre souci concernant les assert, c'est un moyen de voir qu'un contrat est rompu, pas un moyen de spécifier un contrat, du moins de ce que j'en ai compris. Donc comment le spécifier autrement que par des commentaires ?

  8. #8
    Membre émérite

    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    717
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 717
    Par défaut
    Citation Envoyé par bolhrak Voir le message
    Je pensais que la programmation défensive était le fait de blinder son code en vérifiant au maximum les arguments passés à une méthode pour traiter un maximum de cas possibles, donc plus vraiment besoin de contrat. D'où justement mon interrogation sur sa mise en oeuvre en C++ ; les notions de programmation par contrat que j'ai viennent du bouquin de Meyer où il oppose justement programmation défensive et programmation par contrat, maintenant peut être que cela est dû au fait que le langage utilisé permet une spécification du contrat dans la déclaration des méthodes.
    En fait il y a deux types de programmation défensive : une faible qui s'aparente à une tolérence aux fautes et une forte qui s'aparente à la programmation par contrat.

    Si je livre un framework à un client et qu'un appel au framework dans son code échoue, il souhaitera sûrement savoir quelle condition n'est pas respectée (pré ou post) pour savoir où se trouve le bug justement : dans son code, ou dans celui du framework. Il faut donc bien un moyen lui spécifier que le contrat est rompu et à quel endroit non?
    Oui, dans l'ideal . En pratique si les sources sont fournies, un simple assert() peut suffire. Sinon il faut un autre mécanisme.

    Les exceptions ne sont pas indiquées car elles ont l'inconvénient d'appeler les destruceurs des objets des blocs qu'elles traversent, ce qui peut faire du dégat, notamment en cas de rupture d'invariant.

    Une meilleure solution est d'appeler un callback d'erreur, fourni par l'utilisateur.

    Autre souci concernant les assert, c'est un moyen de voir qu'un contrat est rompu, pas un moyen de spécifier un contrat, du moins de ce que j'en ai compris. Donc comment le spécifier autrement que par des commentaires ?
    Dans la doc . Si le language n'a aucun support pour spécifier un contrat, on a pas le choix, c'est commentaires et/ou doc.

  9. #9
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 292
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 292
    Par défaut
    Hum ...
    Aurélien a donné la forme souvent évoquée par James (pour ceux qui fréquentent fclc++ ou clc++m) qui permet un support natif, à la pattern/idiome, de la PpC en C++ (au contraire de Java qui a besoin d'un préprocesseur)

    Il y a effectivement deux formes d'erreurs à distinguer:
    - les erreurs de programmation
    - les anomalies liées à un contexte d'exécution non nominal.

    Dans les deux cas je parle quand même de programmation par contrat. Qu'il s'agisse de vérifier une hyppothèse forte sur laquelle repose un algo, ou un contexte nécessaire à la continuation d'un traitement, dans les deux cas des conditions sont nécessaires à la bonne exécution. Je suis moins à cheval sur la définition de PpC, d'autant que dans tous les cas, je vérifie que le contrat d'utilisation est bien respécté.

    Dans le premier cas (erreur de codage), les assert sont parfaits. Ils permettent durant la phase d'élaboration d'un programme de s'assurer que toutes les conditions de bon fonctionnement de nos algos sont bien respectées (compteur > 42, pointeur non nul).
    Si ce n'est pas bon, autant stopper l'exécution plutôt que de faire semblant et continuer pour observer des choses incompréhensibles. Cela permet d'obtenir un core qui sera analysable dans un débuggueur.
    En phase de release, les tests ne sont plus sensés être nécessaires. Au pire, on peut toujours les doubler d'exceptions si on préfère que le client ait une boite de dialogue plutôt qu'un core.

    Note: les assert peuvent inlinés être en dur dans la définition même de la classe Contrat (qui remplace la notion d'interface que l'on a en Java/COM). Le vrai code ayant lieu dans la fonction privée virtuelle. Du coup, le client de tes bibliothèques sera averti (sans avoir à recompiler tes bibliothèques) qu'il s'en est servi incorrectement.

    Dans le second cas (contexte d'exécution non nominal), il ne faut surtout pas utiliser les assertions. Les exceptions sont un traitrement adapté. Comme ça, on pourra signaler à l'endroit qui va bien que le fichier toto.jpg n'existe pas, que l'on a perdu notre correspondant, que le disque est plein, ... On s'éloigne certes de la définition initiale de la PpC, mais le même type de squelette peut être employé.


    Après, chaque contrat est unique. Pour chaque contrat, on va savoir qu'elles sont les préconditions qui relèvent d'un algo mal codé de celle qui relèvent d'une situation possible bien qu'anormale.
    Du coup, sorti de la squelette général donné par Aurélien, il devient difficile d'automatiser plus quoique ce soit. À la limite un plugin qui insèrera le squelette qui va bien dans un éditeur de textes.


    En lectures:
    - les posts de James Kanze
    - les articles de Matthew Wilson sur artima, ainsi qu'un chapitre de son bouquin Imperfect C++.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  10. #10
    Membre chevronné
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Par défaut
    Ok, ça devient un peu plus clair maintenant. Merci à tout le monde pour les précisions apportées.

  11. #11
    Membre émérite

    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    717
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 717
    Par défaut
    Citation Envoyé par Luc Hermitte Voir le message
    Aurélien a donné la forme souvent évoquée par James (pour ceux qui fréquentent fclc++ ou clc++m) qui permet un support natif, à la pattern/idiome, de la PpC en C++ (au contraire de Java qui a besoin d'un préprocesseur)
    Enfin, du calme, cela permet juste de spécifier le contrat d'une classe dérivée par rapport à sa base. Ce n'est qu'une petite partie de ce que permet le C++. Il reste le contrat d'une fonction libre, d'une classe valeur, d'un type template, ...

  12. #12
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 292
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 292
    Par défaut
    C'est toujours exactement le même principe. Pour les fonctions libres, Matthew Wilson propose par exemple ce genre d'écriture (qui n'est pas sans rappeller non plus le DP template-method pour l'aspect "comment").
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Ret f(Params ....)
    #ifdef DBC
        check pre conds
        Ret r = f_unchecked(p);
        check post conds
        return r;
    }
     
    Ret f_unchecked(Params ....)
    #endif
    {...}
    C'est clair que ce n'est pas beau, mais c'est parfaitement fonctionnel, cela remplit son rôle, et il est parfaitement possible de générer ce genre de choses semi-automatiquement. (après, je m'y serais pris légèrement autrement, mais c'est une autre histoire)


    Pour les contrats sur les templates, tu fais allusion aux concepts ?
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. programmation par contrat
    Par Choupinou dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 18/03/2009, 01h46
  2. [Language]Programmation par contrat
    Par manube dans le forum Langage
    Réponses: 3
    Dernier message: 20/12/2005, 10h16
  3. [Eiffel] Programmation par contrats
    Par SkIllz2k dans le forum Autres langages
    Réponses: 1
    Dernier message: 02/05/2005, 20h05
  4. [Tests]La programmation par contrats
    Par fabien.raynaud dans le forum Test
    Réponses: 6
    Dernier message: 26/07/2004, 11h06

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo