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 :

Class et gestion d'erreur avec "MFC" en commentaire


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Homme Profil pro
    Ingénieur aéronautique
    Inscrit en
    Octobre 2018
    Messages
    216
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur aéronautique

    Informations forums :
    Inscription : Octobre 2018
    Messages : 216
    Par défaut Class et gestion d'erreur avec "MFC" en commentaire
    Bonjour,

    Ci-dessous un exemple de gestion d'erreur issue du livre C++11 de Pearson et quelque peu adapté (code source complet en pièce jointe) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    const int &  Array::operator[](int index) const
    {
    	int size = Array::getSize();
     
    	if (index >= 0 && index < size)
    		return pType[index];
     
    	throw Array::xLimits();
     
    	return pType[0]; //for MFC
    };
    L'auteur a écrit le commentaire "for MFC". Je suppose que MFC signfie Microsoft Fundation Class, mais j'ignore pourquoi il est écrit et qu'est ce que ça implique. Avez-vous une explication ? Je tiens à préciser que je suis débutant en C++

    Ps : j'utilise Visual Studio Community 2017 Version 15.9.4 avec certains modules de bases de l'installateur...

    Merci par avance !
    Fichiers attachés Fichiers attachés

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Le fait est que certains compilateurs émettent des avertissements lorsque la dernière instruction d'une fonction devant renvoyer "quelque chose" n'est pas un return.

    Je ne suis plus dans les arcanes des MFC depuis trop longtemps pour dire si, effectivement, les règles des MFC vont en ce sens (surtout qu'il s'agit de renvoyer une référence constante), mais la possibilité ne peut pas être écartée

    Par contre, là où l'auteur fait fausse route, c'est en lançant une exception, qui représente -- typiquement -- une situation que le développeur n'est pas en mesure d'éviter et dont il espère secrètement qu'elle ne surviendra jamais (tout en sachant qu'elle finira bien par survenir), comme:
    • un fichier manquant ou inaccessible sur le disque dur
    • un serveur distant qui décide de "rebooter" juste au moment où l'on essaye de le joindre
    • un fou furieux qui vient couper tous les cables réseau
    • un imbécile qui répond "salut" quand on lui demande un nombre
    • j'en passe et de meilleures

    Dans le cas présent, il se fait que le fait d'essayer d'atteindre un élément qui n'existe pas n'est pas le genre de situation à laquelle nous ne pouvons rien faire, à par espérer qu'elle ne surviendra pas et la prendre en compte, car ce genre de situation est provoqué par une erreur de la part ... du développeur lui-même.

    Je sais que c'est pourtant ce que fait la fonction at() de la classe std::vector; mais le fait est que les erreurs dues au développeurs doivent être corrigées bien avant que le code ne finisse en production, ce qui fait que -- a priori -- les vérifications n'ont "plus aucune raison d'être" dans le code qui est mis en production.

    La solution est de mettre en place un système qui permet de faire des vérifications, des assertions qui s'assureront que les (pré) conditions soient remplies, en mode debug, mais qui seront remplacées par une "no op" (par ... rien du tout, en fait) dans le code de production.

    Le fait est qu'il existe un tel système, hérité du C et auquel nous avons accès grâce au fichier d'en-tête <cassert> et que nous devrions utiliser ici sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    /* je reprend le code d'origine, que je corrige à peine pour éviter toute
     * confusion
     */
    const int &  Array::operator[](int index) const
    {
        int size = Array::getSize();
        assert(index < size && "index out of bound");
        return pType[index];
    };
    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

  3. #3
    Membre éclairé
    Homme Profil pro
    Ingénieur aéronautique
    Inscrit en
    Octobre 2018
    Messages
    216
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur aéronautique

    Informations forums :
    Inscription : Octobre 2018
    Messages : 216
    Par défaut
    Merci pour ces explications, même si j'avoue ne pas tout comprendre... Est-ce que vous faites allusion aux macros et aux fonctions inlines (je vais devoir relire le chapitre d'ici peu ) pour le débogage d'erreurs usuelles ?

    Sinon, il est vrai que je trouve les exemples du livre pas quelque peu baclés.

  4. #4
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    @Koala01: Tu ne peux pas être sûr d'avoir couvert tous les cas si ton index peut venir d'un code que tu ne contrôles pas.
    Dans mon boulot en .Net, on a déjà eu des IndexOutOfRangeException avec un index retourné par String::IndexOf... (Plus la confirmation que le bug venait bien de IndexOf, vu que c'était présent dans .Net 3.5 et corrigé dans .Net 4)

    Personnellement, pour tout ce qui n'est pas performance-critical, je recommande d'utiliser des vrais contrôles d'index sur les tableaux (pas des assertions). Surtout en C++, où les tableaux "statiques" existent. Mieux vaut programmer défensivement tant que le coût en performance est acceptable, que d'avoir un crash (ou pire, une faille de sécurité) à cause d'un cas qu'on n'aurait pas prévu (comme une variable qui normalement est initialisée, mais dont on se rend compte trop tard que son initialisation est sautée quand l'âge du capitaine passe à 42 un soir de pleine lune...)

    Je sais qu'il y a des cas où la performance prime sur la programmation défensive. Mais ils ne doivent pas être considérés comme "la règle".
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    En fait, je n'utilises jamais qu'une seule macro (en dehors des gardes anti inclusion, bien sur): la macro assert, car, pour moi, la seule bonne macro est celle qui n'existe pas ... A moins, bien sur, de n'avoir absolument aucun autre choix

    Mais l'idée est que "n'importe quel projet" va -- systématiquement -- passer par deux étapes successives : une étape de "développement et de tests", durant laquelle on s'assure
    • que l'application fournit les services que l'on attend de sa part
    • que chaque fonction fournit le résultat attendu
    • que chaque fonctionnalité réagit de la manière espérée

    et une étape de "production" qui survient lorsque l'on estime que notre application est "suffisamment avancée" que pour pouvoir être utilisée par "quelqu'un d'autre".

    A priori, l'étape de développement et de tests sera essentiellement marquée par le fait:
    1. que la compilation se fait en mode debug, pour pouvoir
      • mettre en évidence le plus de problème possible et,
      • le cas échéant, pouvoir utiliser le débuggeur pour voir "ce qui coince
    2. qu(une série de test unitaires destinés à s'assurer que chaque fonction réagit exactement comme elle le doit dans les différentes circonstances sera mise en place et exécutée régulièrement

    Alors que l'étape de production sera essentiellement marquée par le fait que la compilation est fait en mode "release", en activant autant d'optimisation que possible.

    L'adage populaire veut que
    La totalité des problèmes rencontrés par une application trouve son origine dans l'interface entre la chaise et le clavier
    Il nous met, bien sur, en garde contre le fait que l'utilisateur est un "imbécile distrait", qui n'attend que l'occasion pour faire une connerie.
    Mais il a d'autant plus raison que, durant toute la durée du développement, cette interface est composée... de développeurs: un développeur persuadé de ne jamais commettre la moindre erreur en commet déjà une rien qu'en pensant cela!

    La plupart des applications sont tellement complexes qu'il est beaucoup trop facile d'inverser l'ordre des paramètres à transmettre à une fonction ou d'oublier certaines conditions qui doivent être respectées pour s'assurer qu'une fonction sera en mesure de faire son travail.

    Comme je l'ai dit plus haut, certaines conditions sont "indépendantes de la volonté du développeur", mais il y a de très nombreux cas dans lesquels c'est -- justement -- parce que le développeur a oublié de s'assurer que toutes les conditions étaient remplies, que la fonction ne peut pas fournir le résultat attendu.

    Ces cas doivent être repérés et corrigés le plus vite possible, car il est un fait certain : plus un problème est repéré rapidement, plus il est facile d'y apporter une solution; ne serai-ce que parce que l'on a sans doute encore le code qui provoque le problème "devant les yeux" (et que l'on s'en souvient beaucoup plus facilement).

    Par contre, une fois que l'on a la garantie que le développeur n'a pas fait d'erreur (parce que tous les chemins d'exécution qui mènent à une fonction particulière ont été vérifiés), le contrôle de ces conditions n'a absolument plus aucune raison d'être, vu que l'on sait pertinemment que la logique est correcte, que les conditions qui dépendent du seul développeur sont respectées.

    Il n'y a donc aucune raison d'imposer la vérification de ces conditions dans le code qui sera fournit en production, et c'est la raison de l'utilisation de la macro assert
    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. #6
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Par contre, une fois que l'on a la garantie que le développeur n'a pas fait d'erreur (parce que tous les chemins d'exécution qui mènent à une fonction particulière ont été vérifiés),
    Et comment fais-tu cela, "sans la moindre erreur", dans un langage où les exceptions rendent les chemins d'exécution non-évidents? (et ce, surtout pour les conditions inattendues)
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Et comment fais-tu cela, "sans la moindre erreur", dans un langage où les exceptions rendent les chemins d'exécution non-évidents? (et ce, surtout pour les conditions inattendues)
    Tu sembles oublier qui'l n'y a que si une exception est lancée que le chemin n'est pas clair...

    Ce n'est pas parce que la fonction C risque de lancer une exception que tu es dans l'incapacité de définir le chemin qui mène à l'exécution de cette fonction : ce sera peut-re la fonction A qui appelle la fonction B qui appelle la fonction C, ou la fonction D qui appelle directement la fonction C, mais, quoi qu'il arrive, tu peux déterminer très facilement quelle fonction est à l'origine de l'exécution de n'importe quelle fonction.

    Et si je suis bien d'accord sur le fait que -- a priori -- une exception indique à tout le moins le fait qu'une fonction quelconque n'est pas en mesure de fournir le résultat attendu, je ne suis, par contre, pas d'accord avec le fait que le lancement d'une exception soit le seul moyen utilisé par le développeur pour signaler le problème.

    Car il y a deux cas dans lesquels une fonction ne sera pas en mesure de fournir le résultat attendu :

    Le premier, qui justifie pleinement de lancer une exception, est le fait que la fonction ne dispose pas d'une ressource dont elle a besoin pour pouvoir faire son travail : si elle doit accéder à un fichier inaccessible pour une raison ou une autre, si elle doit allouer de la mémoire pour un objet que le système lui refuse, si elle doit accéder à un serveur distant qui est "down" ou qui refuse la connexion. Ce sont des situations totalement indépendantes de la volonté du développeur de la fonction, dont il espère qu'elles ne surviendront jamais, mais qu'il doit prendre en compte parce qu'elles peuvent rapidement mener à une catastrophe par la suite.

    Par contre, le deuxième cas regroupe toutes les situations dans lesquelles la fonction ne sera pas en mesure de fournir le résultat attendu à cause d'un paramètre incohérent qui lui aurait été transmis : parce que l'on essaye d'accéder au onzième élément d'un tableau qui n'en contient que dix, parce que l'on décide de transmettre nullptr au lieu d'un pointeur sur un objet qui doit exister, ou parce que le nom du fichier ne contient pas la bonne extension qui permettra de l'ouvrir.

    En un mot comme en cent, ces situations représentent des conditions que les paramètres transmis doivent respecter au plus tard au moment où la fonction est appelée, ce sont des pré conditions.

    Et, pour les pré conditions, on se fout pas mal que le chemin d'exécution puisse être incertain suite au lancement d'une éventuelle exception, car, tout ce qui nous importe c'est ... les différentes fonctions par lesquelles nous passerons qui mènent à l'exécution de la fonction. Nous ne sommes donc bel et bien intéressés que par les appels des fonctions (et que l'on retrouve notamment au niveau de la pile d'appels en débug).

    Or, si les paramètres transmis sont "incohérents", c'est à cause de la logique de la fonction appelante qui, encore une fois, peut se retrouver face à une situation où
    • une ressource dont elle a besoin n'est pas disponible (et devrait donc lancer une exception AVANT de faire appel à notre fonction)
    • un paramètre qu'elle a reçu est incohérent (et devrait donc le signaler au développeur de la fonction appelante pour qu'il corrige le problème AVANT de faire appel à la fonction)


    Quoi qu'il en soit, si un paramètre est incohérent, la cause est toujours à chercher au niveau ... de la logique des différentes fonctions qui ont mené à l'appel de la fonction. Et cette logique est toujours susceptible d'être corrigée, non pas par le développeur de la fonction qui se rend compte que le paramètre transmis est incohérent, mais ... par le développeur de la fonction qui y fait justement appel.

    Or, c'est justement là l'astuce : si on s'arrange pour faire passer le message "vérifie ta logique, parce que tu me transmets un paramètre incohérent" au développeur d'une fonction appelante à chaque fois qu'un paramètre est incohérent, le seul moment où le développeur en question ne recevra plus ce message sera ... quand sa logique d'appel sera "suffisamment au point" que pour éviter de transmettre des paramètres incohérent.

    Lapalisse n'aurait sans doute pas dit mieux

    Et donc, si on le force à corriger sa logique en mode débug pour que, quoi qu'il puisse arriver -- dans la limite des situations qui ne dépendent que de lui, bien sur -- il n'y a "plus aucune raison" de faire perdurer la vérification de la logique d'appel dans le code qui se retrouvera en développement.

    Nous pourrions envisager de le faire, mais ... cela occasionne un test inutile impliquant la mise en place tout un processus permettant de lancer une exception qui ... ne sera jamais lancée

    L'un dans l'autre, tu me dira que l'on s'en fout, vu que les exceptions ne posent des problèmes que lorsqu'elles sont lancées, et je ne pourrai qu'être d'accord avec toi sur le fond du problème.

    A ceci près que tout le processus permettant la levée éventuelle d'une exception et -- plus encore -- sa récupération dans la pile d'appels sera du pur "code mort" dans l'application finale, vu qu'il n'y aura plus aucun chemin susceptible d'en provoquer l'exécution.

    Il semble donc tout à fait cohérent de faire en sorte que ce code mort soit retiré de l'application qui se retrouve en production, n'es tu pas d'accord avec moi
    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

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

Discussions similaires

  1. [Sybase ASE 12.5.3] Gestion d'erreur avec @@error
    Par lsone dans le forum Sybase
    Réponses: 5
    Dernier message: 24/07/2006, 22h25
  2. [J2EE/JSP] Gestion des erreurs avec une base SQL server 2005
    Par critok dans le forum Servlets/JSP
    Réponses: 3
    Dernier message: 30/04/2006, 16h57
  3. Gestion des erreurs avec setjump/longjump
    Par gege2061 dans le forum C
    Réponses: 1
    Dernier message: 05/02/2006, 15h51
  4. [Upload] Problème pour gestion d'erreur avec class
    Par allserv dans le forum Langage
    Réponses: 2
    Dernier message: 27/12/2005, 13h00
  5. Class de gestion des images avec rotation
    Par Johnny Boy dans le forum MFC
    Réponses: 1
    Dernier message: 03/05/2005, 11h54

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