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 :

Retour de fonctions ou exceptions ? [Tutoriel]


Sujet :

C++

  1. #41
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Pour moi c'est le comportement normal et que j'attends d'un findXX.
    L'action de find n'assure pas de trouver quelque chose, sinon ce serait un get.

    <snip>

    La fonction est mal nommée, si on est sûr de trouver, find n'est pas approprié, get l'est déjà plus.
    Très, très judicieux ; et très, très important.

    Le fait de trouver ou pas quelque chose via le find fait - intrinsèquement - partie de l'algorithme. Ce dernier peut échouer, c'est dans son ADN. Par contre, get suppose que la donnée qu'on souhaite récupérer est présente - échouer ne fait pas partie de son ADN. C'est la différence entre le find() de la librairie standard C++, et le at() du vecteur (operator[] est un cas particulier, dans le sens où un échec fait crasher le système ; c'est donc un choix de design).

    Citation Envoyé par Ekleog Voir le message
    AMHA, il y a quand même une différence entre code de retour et exceptions.

    Un code de retour est une gestion d'erreurs opt-in, une exception est opt-out.
    Non ; un code d'erreur en retour est lié au fait que l'algorithme, tout en restant dans son domaine d'exécution, échoue. Une exception est liée au fait que l'algorithme n'est pas dans son domaine d'exécution, donc ne peut pas ne pas échouer.

    Citation Envoyé par Ekleog Voir le message
    En gros : Un mauvais dév qui arrive sur le code va se planter avec les codes de retour, et pourra faire tout péter, simplement parce qu'il n'aura pas pensé à vérifier un code de retour.
    et

    Citation Envoyé par Ekleog Voir le message
    On en arrive avec du compiler-specific : __attribute__((warn_unused_result)).
    Pas vraiment. En fait, la remarque de oodini :

    Cela n'est-t-il pas évitable avec un compilateur qui te signale qu'une valeur de retour n'est pas récupérée ?
    est tout à fait justifiée : la bonne gestion des codes de retour n'est pas liée au programmeur, elle est liée à l'analyse statique du code source (tant mieux si c'est le compilateur qui la fait ; mais ce n'est pas obligatoire, dans le sens où la compilation d'u projet est une suite d'opération, dont l'analyse statique peut faire partie - moyennant la modification du process de build).

    Citation Envoyé par therward Voir le message
    Je dirais même que les warnings sont très souvent ignorés par les devs soit parce qu'ils ont encore à progresser soit parce que l'environnement n'est pas favorable (pression des délais, etc...)
    Presque OK. Disons que ces programmeurs là n'ont pas de requirement du type "le code doit compiler sans warning avec -Wall", ou qu'ils sont trop jeunes pour se rendre compte qu'un warning signale un problème qu'on va devoir corriger, de toute façon (ce qui implique une perte de temps future, ce qui diminue la portée de l'argument "pas assez de temps").

    Citation Envoyé par therward Voir le message
    • corriger le code appelant, car le plantage dû à son défaut ne peut plus être ignoré par négligence ou inconscience quand il met fin au traitement
    • appliquer une logique de compensation si on peut en concevoir une
    • parfois même ne rien faire de plus car l'arrêt du traitement dû à l'exception (avec un message idoine) peut parfois être une réaction suffisante en première analyse (ex: données corrompues en entrée non testées par le côté appelant)
    Ceci-dit, je rejoins l'analyse globale de la suite du post, mais je l'estime trop theorico-pragmatique (cad trop orientée "je pense que dans la pratique, ça se passe comme ça" ; parce que sinon, ça ressemble fort à un oxymore). L'analyse se heurte en fait à ce qu'a annoncé jblecanard:

    ... ils sont capable de simplement attraper une exception juste pour qu'on ne la voie plus ...
    Ca, je confirme ; je l'ai déjà vu faire. Etant donné que personne (et je dis bien personne) ne fait de revue de code derrière, un tel contournement - même complètement incorrect - a 99,99% de chances de passer.

    Le fait, d'après mon expérience (et tout un chacun est tout à fait autorisé à la remettre en cause), les exceptions n'offrent pas plus de sécurité que les codes de retour - et pas moins, dans le sens où un méchant (pas forcément mauvais, mais juste méchant) programmeur peut tout à fait ignorer et l'un et l'autre - soit pas omission de code, soit par l'écriture de code spécifique. Et dans la faune des programmeurs, on se doit de noter que ceux qui sont prompts à ignorer (voire invalider) les codes retour sont aussi ceux qui vont catcher les exceptions pour les rendre silencieuses. Malheureusement, c'est d'eux qu'on cherche à se protéger, pas du débutant (qui fait l'erreur par ignorance une fois) ni du vieux routard (qui sait exactement pourquoi il ignore une erreur ou une exception). Ce qui est tout à fait résumé par Bousk :

    Citation Envoyé par Bousk Voir le message
    Ce n'est pas du tout une raison pour préférer une exception à une erreur, au lieu d'un switch/default il mettra un try {} catch {} et même combat.
    La discussion est sans conteste intéressante, puisqu'elle permet de faire avancer le debat ; par contre, elle oublie de dire que l'article de Aaron Lahman (gracieusement traduit par LittleWhite ; merci, au fait, parce que c'était important qu'il soit rendu accessible au plus grand nombre) se plante complètement sur ce qui fait la base de son article - ce qu'il appelle le C++ moderne.

    Le C++ moderne n'utilise pas de shared_ptr<> juste pour ne pas avoir de pointeurs qui trainent ici et là. Effectement, comme il le dit, un pointeur nu est suspect ; mais d'un autre coté, shared_ptr<> (ou unique_ptr<>) sont quasiment transparent aux exception. La seule différence entre ces objets et les pointeurs nus est qu'ils permettent - à la sortie du scope - de détruire l'objet s'ils n'a pas été retourné par la fonction (c'est ce qui arrive en cas d'exception).

    C'est du C++ moderne bas de gamme, du niveau d'une personne qui n'a pas encore compris RAII et ce que ce concept apporte au langage. J'ai cru pendant un instant que l'auteur allait prendre de la hauteur (mon dieu ! un jeu de mots !)

    Je change les règles. Je ne me soucie plus du nombre de branchements, que ce soit lors d'échecs ou de réussites. Par contre, je me préoccupe énormément du fait qu'à chaque instant, il n'y a qu'un seul propriétaire pour chaque ressource et qu'en cas d'échec, ce propriétaire gérera la ressource.

    C'est un changement fondamental. L'approche moderne utilisant le RAII n'a pas toujours un code explicite pour les conditions d'échecs. Les échecs courants sont toujours gérés non localement. La ressource locale est immédiatement prise en charge par des objets automatiques, grâce au RAII, c'est-à-dire qui seront détruits à la sortie du bloc. N'importe quelle ressource brute (comme l'allocation d'un nouvel objet) doit toujours être déléguée à un objet RAII.
    Mon gars, ce n'est pas suffisant. Si tu sais ça, alors tu te dois de remarquer que les smarts pointers ne servent pas à implémenter RAII, mais à ramener du code non RAII dans un chemin plus raisonnable - notamment dans le cas où on ne peux pas faire autre chose que manipuler des pointeurs nus.

    RAII, dans l'exemple proposé, passe nécessairement par des objets complets - on va les appeler notify_icon et icon. Et, comme par magie, le code proposé se simplifie encore :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    notify_icon CreateNotifyIcon() 
    { 
        notify_icon icon; 
     
        icon.set_text("Blah blah blah"); 
        icon.set_icon(icon("cool.ico", GetType())); 
        icon.set_visible(true); 
     
        return icon; 
    }
    Pas besoin de faire un reset() sur des objets construits par défaut - après tout, c'est un jeu avec les objets shared_ptr<>, d'un intérêt très, très limité.

    Le code peut encore être amélioré (1), dans le sens où un notify_icon doit obligatoirement avoir un icone (donc : paramètre du constructeur) et que ce n'est pas nécessairement à la fonction CreateNotifyIcon() de spécifier qu'il est visible ou non (pourquoi est-ce que ça ferait partie de la création d'un icone ?).

    En fait, le code pourrait être :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    notify_icon CreateNotifyIcon() 
    {
        return notify_icon(icon(...), "Blah blah blah");
    }
    Sans sacrifier à la lisibilité ni à quoi que ce soit d'ailleurs. Et du coup, il devient clair - d'une clarté limpide, j'espère - que dans ce cas, les exceptions sont bien plus adaptés qu'un code d'erreur en retour.

    Et du coup, on peut maintenant se poser la question "quand utiliser les exceptions, et quand utiliser les code d'erreur en retour ?".

    Personnellement, j'utilise la règle suivante :

    * si je ne dois pas échouer, alors je génère une exception en cas d'échec.
    * si je peut échouer (cad si c'est dans mon ADN d'échouer (1)), alors je renvoie un code d'erreur en retour.

    On en revient donc à ce qu'a dit Bousk - et d'autres avant lui :

    Citation Envoyé par Bousk Voir le message
    Pour moi c'est le comportement normal et que j'attends d'un findXX.
    L'action de find n'assure pas de trouver quelque chose, sinon ce serait un get.
    La boucle est bouclée - et c'est rare que je boucle les boucles, proffitez en

    --

    (1) c'est ce que me dit la présence de méthodes nommées set_xxx(). Dès qu'on les voit, on se dit : "bon, quelqu'un n'a pas poussé sa réflexion au bout. Dommage il était bien parti."

    (2) et je parle en tant que petite partie d'un programme ; pas en tant qu'humain ; en tant qu'humain, mon ADN me dit principalement "amuse toi, mange, dors, et essaie de faire des enfants" (3)

    (3) je ne sais pas si c'est juste moi ou le monde entier, mais ce qui est dur quand on fait un enfant deviens mou quand on l'élève, et ce qui est mou devient dur. C'est bizarre.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  2. #42
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Dommage que mon chef de projet interdise les fonction commençant par get...

  3. #43
    Membre confirmé
    Inscrit en
    Octobre 2007
    Messages
    210
    Détails du profil
    Informations forums :
    Inscription : Octobre 2007
    Messages : 210
    Points : 459
    Points
    459
    Par défaut
    Citation Envoyé par oodini Voir le message
    Dommage que mon chef de projet interdise les fonction commençant par get...
    Bah faut bien qu'il justifie son poste ...

  4. #44
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Points : 1 475
    Points
    1 475
    Par défaut
    Citation Envoyé par oodini Voir le message
    Dommage que mon chef de projet interdise les fonction commençant par get...
    Il va t'obliger à différencier!
    loadXXX(...) pour les bases de données par exemple?

  5. #45
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par oodini Voir le message
    Dommage que mon chef de projet interdise les fonction commençant par get...
    C'est un tout autre débat...

    Mais, à ce sujet, je serais simplement tenté d'attendre sa justification pour une telle position

    S'il refuse un nom de fonction commençant par get mais qu'il accepte, en revanche une fonction (dont le nom ne commence pas par get ) mais qui expose un détail d'implémentation (et donc qui, dans les faits, agit exactement comme un accesseur ) sans surciller, c'est au minimum un imbécile, au pire, quelqu'on au sujet duquel je me méfierais des compétences.

    Par contre, s'il te demande de renommer ta fonction "parce que ca fait vraiment trop accesseur, et que c'est trop générique", mais qu'il accepte un usage raisonné des accesseurs, c'est à dire : s'il accepte la présence d'un accesseur s'il apparait qu'il correspond effectivement à un service que l'on est en droit d'attendre d'un type donné tout en en refusant la majeure partie parce qu'ils exposent, décidément, un détail d'implémentation, je crois qu'il a au moins fait un pas dans la bonne direction et mérite (en attendant de savoir quel est son point de vue sur le respect d'autres principes du moins) le respect

    Je fais, bien évidemment l'impasse sur l'ensemble des qualités humaines dont il est peut etre pourvu (ou non ), vu que je ne le connais absolument pas

    Mais je le comprend d'autant mieux que j'ai tendance à scruter les fonction qui commencent par get avec la plus grande suspicion
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #46
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Il dit simplement que ça ne sert à rien et que la signature de la fonction suffit pour savoir si c'est un getter ou un setter.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    bool memberName(int value);    // setter
    int memberName()               // getter
    Mais il y a tout de même plein de fois où ça pose problème.

  7. #47
    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
    Je me demande si le code sans exception "final" est vraiment optimal.
    Ne manque-t-il pas les else goto cleanup; après chaque if ? Et le premier test if ( SUCCEEDED(hr) ) me semble inutile, tout comme la déclaration const wchar_t * tmp1 = 0;

    Je viens également de voir qu'il y a un // [5] dans le code avec exceptions "final", mais pas de note de bas de page correspondante.

    Article intéressant sinon ! Merci.

  8. #48
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par oodini Voir le message
    Il dit simplement que ça ne sert à rien et que la signature de la fonction suffit pour savoir si c'est un getter ou un setter.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    bool memberName(int value);    // setter
    int memberName()               // getter
    Mais il y a tout de même plein de fois où ça pose problème.
    Cette syntaxe est très proche de ce que j'ai vu en C# à l'école. Et j'aime pas.
    Oui la signature suffit, mais quand c'est écrit get/set ou assimilé c'est instantané pour voir ce que la méthode réalise.
    A ce moment-là autant interdire tous les verbes dans les noms de méthode, ça avancera pas plus le schmilblik pour autant.. mais au moins un type sera payé pour écrire ces règles, on combat le chomage.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  9. #49
    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
    Citation Envoyé par oodini Voir le message
    Il dit simplement que ça ne sert à rien et que la signature de la fonction suffit pour savoir si c'est un getter ou un setter.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    bool memberName(int value);    // setter
    int memberName()               // getter
    Mais il y a tout de même plein de fois où ça pose problème.
    C’est donc un idiot : la signature d’un getter, c’est :
    Pour en revenir au débat code d’erreur vs exception, la règle que je me fixe est simple :
    - je respecte les post-conditions de ma fonction : c’est donc un code erreur, une info ok (par exemple : find qui renvoie npos).
    - je ne respecte pas les post-conditions de ma fonction : c’est une exception (par exemple : erreur à l’ouverture du fichier, mon objet fichier n’est pas dans un état valide).

Discussions similaires

  1. Retour de fonction en C
    Par troumad dans le forum Linux
    Réponses: 2
    Dernier message: 06/11/2005, 21h43
  2. Utilisation d'un retour de fonction dans un decode
    Par CFVince dans le forum Oracle
    Réponses: 4
    Dernier message: 20/10/2005, 17h22
  3. Référence en retour de fonction, à transformer en hash
    Par raoulchatigre dans le forum Langage
    Réponses: 4
    Dernier message: 15/07/2005, 14h24

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