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 :

Gestion des erreurs - best practice


Sujet :

C++

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    89
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 89
    Points : 50
    Points
    50
    Par défaut Gestion des erreurs - best practice
    Bonjour,

    J'ai une classe (A) qui possède une fonction (float getLength()) retournant un valeur. Dans certains cas, il peut se produire des erreurs au sein de cette fonction. Je me demandais donc quelle est la "best practice" pour gérer ce situations:
    - possibilité 1 : je passe un code d'erreur : float getLength(int *erCode) et effecture un test en sortie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    int erCode=0;
    float l=A.getLength(&erCode);
     
    if erCode==1 // traitement de l'erreur
    - possibilité 2 : je la méthode retourne le code d'erreur :int getLength(float *length) et effectue un test en sortie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    float length=0.0;
    int erCode=A.getLength(&length);
     
    if erCode==1 // traitement de l'erreur
    Que recommande la pratique courante ? L'une de ces deux méthodes ou autre chose ?

    Merci d'avance pour vos conseils.
    Christian

  2. #2
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    780
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2006
    Messages : 780
    Points : 1 176
    Points
    1 176
    Par défaut
    Tu peux utiliser les exceptions en C++.

  3. #3
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Ça dépend aussi de plusieurs choses :
    - l'échec est-il un cas normal/fréquent ?
    - veux-tu obliger le client à gérer le cas d'échec ?

    Si la réponse est oui à la première question, il vaut mieux ne pas utiliser les exceptions. Si la réponse est oui à la deuxième, il vaut mieux utiliser les exceptions.

    Si la réponse est oui aux deux, alors, mettre l'erreur en paramètre inout me semble assez adapté, car cela force le développeur à déclarer cette variable d'erreur, et donc, l'incite fortement à la gérer (alors qu'il est très facile d'ignorer une valeur de retour), tout en évitant la lourdeur des exceptions.

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    349
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : Suisse

    Informations forums :
    Inscription : Novembre 2005
    Messages : 349
    Points : 379
    Points
    379
    Par défaut
    Citation Envoyé par coberle Voir le message
    Dans certains cas, il peut se produire des erreurs au sein de cette fonction.
    C'est quel genre d'erreur qui peut se produire? Division par 0? Pointeur null? Bad alloc? ...?

    L'erreur est-elle dépendante de l'objet appelant, ou uniquement de l'objet appelé?

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Bonjour,
    Si tu ne souhaites pas utiliser d'exceptions pour les raisons citées précédemment, tu peux utiliser Boost.Optional pour gérer les cas d'erreur. C'est le genre d'exemple approprié il me semble.

  6. #6
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Points : 1 051
    Points
    1 051
    Par défaut
    Si tu choisis un code d'erreur, penses à utiliser une enum plutôt qu'un bête int.

  7. #7
    Membre éclairé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 764
    Points
    764
    Par défaut
    Hum, d'après ce que j'ai lu sur Boost.Optional dans le lien que tu as donné, il me semble que ça fonctionne un peu comme un pointeur... avec l'inconvénient du "qui doit faire le delete?" en moins .
    Ca peut être pratique, mais ça alourdi quand même un peu l'écriture (particulièrement gênant si c'est une fonction souvent utilisée).

    Autre chose que tu pourrais éventuellement faire (un peu moins lourdingue que le std::pair<bool, T>) :
    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
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    template<class T>
    class Check
    {
    public :
     
        Check()
        {
            isValid = false;
        }
     
        Check(const T& init)
        {
            isValid = true;
            value = init;
        }
     
        T& Get()
        {
            return value;
        }
     
        bool IsValid() const
        {
            return isValid;
        }
     
        operator T() const
        {
            return value;
        }
     
        operator bool() const
        {
            return isValid;
        }
     
        static const Check<T> INVALID;
     
    private :
     
        T    value;
        bool isValid;
     
    };
     
    template<class T>
    const Check<T> Check<T>::INVALID = Check<T>();
    Je l'ai rapidement testé, ça à l'air de fonctionner
    Les soucis : ce template ne peux pas être utilisé avec le type bool à cause de l'opérateur de conversion implicite. Je ne pense pas non plus que ça soit une bonne idée de l'utiliser avec des types autres que ceux de base.

    Exemple d'utilisation :
    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
    16
    17
    Check<float> SquareRoot(float value)
    {
        if (value < 0.0f)
            return Check<float>::INVALID;
        else
            return sqrt(value);
    }
     
    int main()
    {
        Check<float> f = SquareRoot(-1.0f);
        if (f)
        {
            float toto = 2+f;
            printf("%f\n", toto);
        }
    }

  8. #8
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Points : 1 051
    Points
    1 051
    Par défaut
    @Kalith

    sous Visual 2008 j'ai l'erreur C2593: 'operator +' is ambiguous

    Il est déconseillé d'utiliser l'opérateur bool justement pour éviter cette ambiguïté avec les opérateurs mathématiques. On utilise plus souvent les opérateur void* et ! (cf. ofstream et ifstream).

    Mais si j'ai le choix entre gérer le type bool ou permettre la syntaxe if (f), je préfère de loin la première option.

  9. #9
    Membre éclairé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 764
    Points
    764
    Par défaut
    Ah, sous gcc je n'avais aucun problème, désolé...
    D'habitude j'utilise la technique du pointeur vers une fonction membre pour pouvoir faire le if (f), je ne l'ai pas fait ici pour rendre le code plus simple à comprendre.

    J'ai essayé, mais le soucis c'est que la conversion implicite en T prend le pas sur la conversion en pointeur de fonction membre (du coup "f" est faux si f vaut zéro...) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Check<float> f(0.0f);
    if (f)
    {
        // ne s'exécutera pas car float(0.0f) est évalué comme faux...
    }
    Je ne sais pas s'il y a moyen de contourner ce problème.
    Donc au choix :
    • Soit on veut pouvoir faire if (f), mais du coup on est obligé de faire f.Get() pour avoir la valeur.
    • Soit on veut pouvoir utiliser directement f comme la valeur, mais du coup on est obligé de faire if (f.IsValid()).

    À choisir, je préfère la seconde option.

    Ça donne donc :
    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
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    template<class T>
    class Check
    {
    public :
     
        Check()
        {
            isValid = false;
        }
     
        Check(const T& init)
        {
            isValid = true;
            value = init;
        }
     
        T& Get()
        {
            return value;
        }
     
        bool IsValid() const
        {
            return isValid;
        }
     
        operator T() const
        {
            return value;
        }
     
        static const Check<T> INVALID;
     
    private :
     
        T    value;
        bool isValid;
     
    };
     
    template<class T>
    const Check<T> Check<T>::INVALID = Check<T>();

  10. #10
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Points : 1 051
    Points
    1 051
    Par défaut
    Sans hésiter la seconde option.

    C'est très bien d'avoir pensé au const dans l'opérateur T(), ça évite de pouvoir écrire des ++f trompeurs

  11. #11
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Bonjour,
    C'est amusant, mais vous êtes en train de refaire le Boost.Optional ...

  12. #12
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Et c'est très bien, j'approuve.

    Citation Envoyé par Kalith Voir le message
    Je ne sais pas s'il y a moyen de contourner ce problème.
    Donc au choix :
    • Soit on veut pouvoir faire if (f), mais du coup on est obligé de faire f.Get() pour avoir la valeur.
    • Soit on veut pouvoir utiliser directement f comme la valeur, mais du coup on est obligé de faire if (f.IsValid()).

    À choisir, je préfère la seconde option.
    Il m'arrive de surcharger l'operator "coma" (virgule) au lieu de surchager l'operator bool:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    bool operator,(bool b) const
        {
            return b==isValid;
        }
    Ce qui donne if (f,true) au lieu de if (f.IsValid()). J'en conviens, c'est assez particulier.

  13. #13
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par camboui Voir le message
    Et c'est très bien, j'approuve.
    Pour apprendre, c'est très bien. Mais pour réinventer la roue pour un projet opérationnel
    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
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
    #include <boost/optional.hpp>
    boost::optional<float> SquareRoot(float value)
    {
        if (value < 0.0f)
            return boost::optional<float>();
        else
            return boost::optional<float>(sqrt(value));
    }
     
    int main()
    {
        boost::optional<float> f = SquareRoot(-1.0f);
        if (f)
        {
            float toto = 2+*f;
            printf("%f\n", toto);
        }
     
        f = SquareRoot(4.0f);
        if (f)
        {
            float toto = 2+f.get();
            printf("%f\n", toto);
        }
    }
    C'est pas plus compliqué que ça, non?

  14. #14
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    89
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 89
    Points : 50
    Points
    50
    Par défaut
    Merci pour vos réponses ... c'est bien plus que j'espérai et le débat sur boost dépasse un peu mes compétences mais je vais regarder cela de plus prêt.
    @NiamorH: j'ai bien aimé ton approche ... qui m'a cependant posé quelques soucis (cf. post 4035950, incompatibilité enum et c++)
    Christian

  15. #15
    Membre expérimenté
    Profil pro
    Inscrit en
    Février 2004
    Messages
    1 824
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 1 824
    Points : 1 544
    Points
    1 544
    Par défaut
    Perso, je n'aime pas du tout les codes erreurs. Dans mon taf, ils ne connaissent pas les exceptions, et les problèmes s'accumulent rapidement :

    - En cas d'erreur, libre à l'utilisateur de la fonction de la gérer ou pas. Bien souvent, lorsqu'il programme avec sa problématique métier en tête, de la pression à cause des délais et la peur du crash, il ne traite que le cas qui fonctionne et cache/oublie le reste. Celà amène à des répercutions à un bien plus haut niveau, qui nécessite un certain temps de débug pour retrouver l'origine du disfonctionnement.
    - Les fonctions sont polluées par des codes erreur, leurs manipulation n'est pas aisée, intuitive, et le code s'alourdit rapidement avec des tests de partout, au moindre appel, ça mobilise la valeur de retour, ça oblige à spécifier des paramètres de sortie etc...

    Dans une conception objet, chaque objet a ses responsabilités parmis lesquelles notifier ce qui ne va pas. C'est comme une secrétaire, si elle tombe sur un cas qu'elle ne sait gérer, elle va vers son supérieur. S'il ne sait pas le gérer lui même, il s'en réfère à son supérieur avec l'enrichissement d'information qu'il possède à son niveau etc... jusqu'à temps que quelqu'un sache/puisse traiter le cas.
    Dans une aproche objet, tout est humanisé et le principe est le même. Ton objet, s'il rencontre quelque chose d'anormal, il arrête son traitement par manifester son incapacité à continuer. C'est à la fonction appelante à traiter, voir reporter le problème, avec enrichissement s'il peut, au niveau supérieur.

    Ce que j'ai donc fait, c'était déclarer plusieurs classes d'exceptions (rien que pour pouvoir les distinguer dans les catch des appelants s'ils en ont besoin) mais aussi imposer des informations de contexte sur leurs lancement. Et surtout pour pouvoir générer un rapport, du plus haut niveau vers le plus bas niveau. Parcequ'une exception de division par zéro peu résulter en une incapacité, métier, de haut niveau, à savoir "impossible de passer une commande comportant un article épuisé" sera plus parlant que "on peut pas diviser par zéro". Côté développeur, lui aura besoin d'une pile, parceque la remonté de l'utilisateur sur ce problème peut venir de beaucoup de cas différents, donc il aura besoin de pouvoir remonter jusqu'à la source du problème, la division, les paramètres autour etc...
    Avec ce truc, on a fait pu générer des rapport bien explicites pour le client comme pour les développeurs. Un peu de technique permettent de recenser les fichiers et les lignes de lancement d'exceptions, augmentant la vitesse de recherche du développeur grâce aux informations qu'encapsulent les exceptions etc.

    Naturellement les exceptions doivent rester exceptionnelles, et leur utilisation doit être modérée dans certains domaines par exemple le temps réel compte tenu du fait que leur gestion bouffent un peu les perfs. Mais dans la gestion elles sont plus qu'apropriées.
    "Heureusement qu'il y avait mon nez, sinon je l'aurais pris en pleine gueule" Walter Spanghero

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

Discussions similaires

  1. Gestion d'erreurs : Best Practices
    Par Le Grand Habchkleu dans le forum C#
    Réponses: 2
    Dernier message: 17/11/2009, 16h56
  2. [struts][Datasource]Gestion des erreurs
    Par GreenJay dans le forum Struts 1
    Réponses: 8
    Dernier message: 15/09/2004, 16h51
  3. [VB6] Gestion des erreurs dans une dll
    Par zimba-tm dans le forum VB 6 et antérieur
    Réponses: 8
    Dernier message: 02/08/2004, 11h20
  4. [XSLT]Est ce qu'il y'a la gestion des erreur en xslt ?
    Par miloud dans le forum XSL/XSLT/XPATH
    Réponses: 2
    Dernier message: 04/02/2004, 17h19
  5. [LG]gestion des erreurs
    Par frontin dans le forum Langage
    Réponses: 3
    Dernier message: 29/11/2003, 22h41

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