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++

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

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

    Informations forums :
    Inscription : Octobre 2018
    Messages : 216
    Points : 30
    Points
    30
    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 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
    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
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur aéronautique
    Inscrit en
    Octobre 2018
    Messages
    216
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Haute Garonne (Midi Pyrénées)

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

    Informations forums :
    Inscription : Octobre 2018
    Messages : 216
    Points : 30
    Points
    30
    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 sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    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 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
    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 sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    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 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 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

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Le problème pour moi, c'est la supposition que les tests ont suffit à trouver tous les cas d'erreurs possibles, et que donc, les vérifications en run-time des préconditions de chaque fonction peuvent être supprimées.
    Je considère ça présomptueux (et "optimisation prématurée").

    C'est peut-être lié au fait que maintenant je code surtout selon la culture .Net, où une fonction (publique) qui n'accepte pas un argument nul est censée vérifier inconditionnellement qu'il ne l'est pas. "Toute NullReferenceException est un bug", parce que la fonction aurait dû vérifier et lancer une ArgumentNullException à la place.
    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.

  9. #9
    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 Médinoc Voir le message
    C'est peut-être lié au fait que maintenant je code surtout selon la culture .Net, où une fonction (publique) qui n'accepte pas un argument nul est censée vérifier inconditionnellement qu'il ne l'est pas. "Toute NullReferenceException est un bug", parce que la fonction aurait dû vérifier et lancer une ArgumentNullException à la place.
    C'est sans doute la plus grosse incohérence de tous ces langages "aseptisés" qui considèrent les développeurs comme des imbéciles distraits ( état de fait que l'on ne peut décemment pas nier ), mais qui, d'un autre côté estiment que leurs erreurs de logique ne peuvent être qu'exceptionnelles!

    Mais il y a un moment où il faut choisir, parce que, par nature, une situation exceptionnelle ne risque pas de se rencontrer "à tous les branchements".

    Alors, ou bien, un paramètre qui ne respecte pas les pré conditions est une situation exceptionnelle, ce qui sous entend que tous les développeurs sont des dieux qui ne se trompent jamais, ou peu s'en faut (ce qui est loin d'être le cas, il faut bien l'admettre), ou bien le développeur est -- effectivement -- un imbécile distrait à ses heures, et un paramètre ne respectant pas les préconditions est "dans l'ordre des choses", mais on peut espérer, à partir du moment où l'on signale un problème qui ne peut qu'être une erreur de logique en période de développement, qu'il a conscience de sa propre faiblesse, et qu'il aura mis en place toute une politique de mise en évidence des problèmes de logique rencontrés.

    Pour la petite histoire, j'ai eu un prof d'algorithmie qui disait
    Quel que soit le langage utilisé, si la logique est correcte (sous entendu : et correctement traduite), la première compilation réussie fournira le résultat souhaité
    Hé bien, tu sais quoi Il avait tout à fait raison!

    Les exceptions sont là pour mettre fin à l'exécution d'un processus dont on se rend compte à un moment donné qu'il ne pourra pas fournir le résultat souhaité.

    Mais,

    Ce que les gens semblent oublier, c'est qu'il existe deux grandes catégories d'erreurs: celles contre lesquelles on peut se prémunir, et les autres.

    On peut se prémunir contre un paramètre incorrect : il suffit de vérifier -- au plus tard au début de l'exécution de la fonction -- (ce qui sous entend aussi que l'on pourrait faire la vérification ... juste avant de faire appel à la fonction ) si le paramètre est correct ou non.

    Par contre, on ne peut pas se prémunir contre une entrée -- quelle que soit son origine -- incorrecte ou incohérente, ni contre une décision "arbitraire" du système qui héberge l'application. Tout ce que l'on peut faire, si l'on est confronté à une entrée incohérente ou à un refus arbitraire du système, c'est "en prendre acte", mettre fin à l'exécution, et "faire remonter" l'information dans l'espoir qu'il sera possible de "rectifier le tir" en amont.

    Dans certains cas, il est possible de rectifier le tir au niveau de l'application: s'il s'agit d'une entrée de la part de l'utilisateur, on peut lui demander de recommencer. S'il s'agit d'une connexion à un serveur distant, on peut attendre un peu et réessayer de s'y connecter.

    Dans d'autres -- ou peut-être -- simplement si le problème persiste après "un certain nombre d'essais" la correction du problème passera par une action "extérieure à l'application", et justifiera clairement que l'application soit purement et simplement tuée (en attendant que la solution attendue ait été apportée)

    Citation Envoyé par Médinoc Voir le message
    Le problème pour moi, c'est la supposition que les tests ont suffit à trouver tous les cas d'erreurs possibles, et que donc, les vérifications en run-time des préconditions de chaque fonction peuvent être supprimées.
    Je considère ça présomptueux (et "optimisation prématurée").
    Je comprends ton point de vue... Surtout s'il s'agit d'une unique assertion perdue dans un océan de code.

    Revenons en donc à notre exemple d'origine:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    /* 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];
    };
    Bien sur, cet exemple est minimaliste, ce qui ne permet pas vraiment de s'en rendre compte, et qui justifie parfaitement ton point de vue.

    Moi, en tant que développeur de l'opérateur [] de la classe Array, j'ai fait ma part du contrat (mais guère plus, je te l'accorde) en vérifiant que la pré condition (index < size_) était respectée. Et j'en ai rien à foutre de la manière dont l'utilisateur veillera à respecter la sienne (de part du contrat).

    J'irais même plus loin : j'en ai rien à foutre qu'il respecte ou non sa part du contrat, car il est prévenu : s'il ne me fournit pas des données cohérentes, tout peut arriver.

    Mais, comme il est prévenu de ce qu'il risque et que je pars (peut-être à tort, me diras tu) que l'utilisateur de ma classe Array a un minimum de compétence (même s'il est distrait à ses heures), je suis tout à fait en droit d'attendre de sa part qu'il prendra "toutes les mesures" qui seront nécessaires pour respecter sa part de contrat.

    Par exemple, s'il part sur une simple boucle proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void foo(){
        Array a;
        /* remplissage de a avec 10 éléments */
        for(size_t i=0; i<=a.size();++i) { //OUPS : i == a.size() à un moment donné
            int value = a[i]; // pré condition non remplie quand i == a.size()
        }
    }
    je sais qu'il se prendra une assertion en mode debug. S'il ne teste pas suffisamment son programme, ce n'est pas mon problème!

    Mais je refuse de considérer une erreur d'attention ou une erreur de logique comme ... quelque chose d'exceptionnel !

    Maintenant, Jack va peut-être utiliser la fonction askIndex() écrite par Joe, 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
    11
    12
    13
    14
    15
    16
     
    /* la fonction écrite par Joe */
    size_t askIndex(){
         /* affiche un message demandant à l'utilisateur l'indice
          * de l'élément auquel il veut accéder
          * le récupère depuis le système d'entrée en vigueur
          * le renvoie (sans vérification préalable)
          */
    }
    /* la fonction écriite par Jack */
    void foo(){
        Array a;
        /* remplissage de a avec 10 éléments */
        size_t id = askIndex();
        /* la suite */
    }
    Hé bien, c'est à jack de s'assurer que la pré condition permettant de faire appel à l'opérateur [] est remplie par la valeur renvoyée par askIndex (en gros, il doit faire de la pré condition que j'impose une post condition de askIndex). Encore une fois, la manière dont il s'y prendra, j'en ai rien à foutre!

    Soit Jack peut en parler à Joe, qui modifiera sa fonction askIndex pour lui donner une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    size_t askIndex( size_t max){
        size_t id;
        do{
         /* affiche un message demandant à l'utilisateur l'indice
          * de l'élément auquel il veut accéder
          * le récupère depuis le système d'entrée en vigueur
          */
            if(id >= max){
                 /* affiche un message demandant de corriger le choix */
            }
        } while(id >= max);
        return id;
    }
    Soit Jack ne peut pas communiquer -- pour une raison ou une autre -- avec Joe, et il prend -- au niveau de foo -- toutes les mesures aptes à assurer la pré condition de mon opérateur [].
    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

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Moi, en tant que développeur de l'opérateur [] de la classe Array, j'ai fait ma part du contrat (mais guère plus, je te l'accorde) en vérifiant que la pré condition (index < size_) était respectée. Et j'en ai rien à foutre de la manière dont l'utilisateur veillera à respecter la sienne (de part du contrat).

    J'irais même plus loin : j'en ai rien à foutre qu'il respecte ou non sa part du contrat, car il est prévenu : s'il ne me fournit pas des données cohérentes, tout peut arriver.
    Je dirais que c'est là le problème de la culture C++: Cette culture du comportement indéfini, qui est rejetée par la culture .Net.

    La culture de vérification des préconditions en .Net, ce n'est pas "s'il y a un comportement indéfini, c'est de ta faute" mais "le risque zéro n'existe pas, donc on vérifie toujours" (éventuellement, l'optimiseur peut élider des vérifications redondantes, quand l'analyse statique prouve que c'est impossible/déjà vérifié -- mais contrairement au C++, il n'y a pas de comportement indéfini, et donc le compilo ne déclare jamais "cette branche entière est invalide, donc je supprime les tests").
    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.

  11. #11
    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
    Tu pourrais vérifier et retourner un pointeur sur la donnée si elle existe, ou nullptr s'il est hors bornes par exemple.
    Seulement en terme de performance, ça fait chier de retourner un pointeur qu'il va falloir déréférencer alors que tu peux retourner une référence directement, et s'il est hors bornes, c'est qu'il a fait une connerie. Et donc un assert suffit amplement.
    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.

  12. #12
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Je dirais que c'est là le problème de la culture C++: Cette culture du comportement indéfini, qui est rejetée par la culture .Net.
    Je dirais plutôt qu'en c++, on préfère laisser planter un programme que de le laisser tomber dans un état incohérent. Alors que C# a hérité de la culture web (java) du "il faut garder le programme en vie même si ça a merdé quelque part".
    Je ne suis partisan ni de l'un ni de l'autre, chacun a ses avantages. La deuxième a notamment d'énormes avantages pour tout ce qui touche au réseau, et au web en particulier.

    Après, avec les static_assert et un peu de bonne volonté (et du temps), on peut aujourd'hui produire beaucoup de garde-fou, en c++, pour s'assurer qu'un programme reste cohérent, et ce, au moment de la compilation. C'est quand-même sacrément puissant non?
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  13. #13
    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 Médinoc Voir le message
    Je dirais que c'est là le problème de la culture C++: Cette culture du comportement indéfini, qui est rejetée par la culture .Net.

    La culture de vérification des préconditions en .Net, ce n'est pas "s'il y a un comportement indéfini, c'est de ta faute" mais "le risque zéro n'existe pas, donc on vérifie toujours" (éventuellement, l'optimiseur peut élider des vérifications redondantes, quand l'analyse statique prouve que c'est impossible/déjà vérifié -- mais contrairement au C++, il n'y a pas de comportement indéfini, et donc le compilo ne déclare jamais "cette branche entière est invalide, donc je supprime les tests").
    Et, à force d'essayer de garder un processus en vie envers et contre tout, on en vient à ne se rendre compte qu'il y a un problème de logique que... dix mois après la mise en production de l'application. C'est à dire, bien après que le développeur n'ait eu tout le temps qu'il lui fallait pour oublier les trois quarts du code qu'il a écrit.

    En plus, ce que tu sembles ne pas comprendre, c'est que l'on s'en fout qu'il y ait un comportement indéfini, à partir du moment où il n'y a aucune raison qu'il puisse survenir en production.

    Bien sur, cela signifie qu'il faut une politique de tests (qu'il s'agisse de tests unitaires ou du "test du singe") stricte qui puisse veiller à mettre toutes les circonstances "bancales" en évidence.

    Mais, a priori, n'importe quelle équipe de dev "un tant soit peu consciencieuse" aura mis une telle politique en place "très rapidement" après le lancement du projet
    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

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Et, à force d'essayer de garder un processus en vie envers et contre tout, on en vient à ne se rendre compte qu'il y a un problème de logique que... dix mois après la mise en production de l'application. C'est à dire, bien après que le développeur n'ait eu tout le temps qu'il lui fallait pour oublier les trois quarts du code qu'il a écrit.
    Je dirais plutôt le contraire, l'exception permet un "fail-fast" là où un code C++ pourrait ne pas remarquer avant plusieurs secondes (une éternité en termes de débogage) qu'en dépassant d'un tableau de int, on a écrasé le pointeur vers la vtable d'un objet... Et tout ça parce que les tests n'avaient pas suffit à trouver le cas où le fichier corrompu passait inaperçu...

    Évidemment, si tout le code est dans une boucle avec un try/catch qui avale l'exception, ça peut perdre l'info d'erreur au lieu de la mettre en évidence... Mais dès qu'on daigne jeter un coup d’œil aux erreurs, on a une bien meilleure idée de leur point d'origine que dans les cas bizarres de C et C++ liés à une corruption de la mémoire...
    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.

  15. #15
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

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

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    Je trouve que vous faite un homme de paille de .Net.

    D'aucune façon .Net n'encourage à faire des programmes "zombis" qui ne veulent pas mourir.

    Le fait de préférer lancer des exceptions en .NET est pour avoir plus de détails en cas de problème.
    Contrairement au C++ natif, si vous faites un peu nimportenawak avec la mémoire, le Runtime .Net vous renvoie, avec raison, dans les cordes, et avec un message d'erreur bien moins explicite que celui que vous pouvez

    Cela a un coup en terme de performance, mais qui est factorisé et optimisé par les développeurs du framework .Net.

    Les tests (unitaires, ...) sont tous aussi présent en .Net qu'en C++.

  16. #16
    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 Médinoc Voir le message
    Je dirais plutôt le contraire, l'exception permet un "fail-fast" là où un code C++ pourrait ne pas remarquer avant plusieurs secondes (une éternité en termes de débogage) qu'en dépassant d'un tableau de int, on a écrasé le pointeur vers la vtable d'un objet... Et tout ça parce que les tests n'avaient pas suffit à trouver le cas où le fichier corrompu passait inaperçu...
    en quoi une exception pourrait-elle changer quoi que ce soit à une période de tests pourries

    Si tu as une exception en production ET qu'elle n'est pas récupérée par ailleurs pour tenter de maintenir le processus en vie aussi longtemps que possible, alors, oui, tu auras un indice du mauvais état de ta période de tests. Mais c'est tout!

    Cet indice, tu l'obtiendra aussi si tu te retrouve avec un résultat totalement farfelu !

    Et, si je suis d'accord avec toit que l'exception est plus "flagrante" que le résultat farfelu, ne vient pas me dire que l'exception sera plus précise pour te permettre de retrouver le problème, car ce ne sera pas forcément le cas.

    Car le seul moyen d'espérer pouvoir tirer une information cohérente à partir d'une exception, c'est qu'elle soit loguée avant d'être lancée : si on s'y intéresse au moment où on la récupère, on a déjà perdu toutes les informations de la pile d'appels, qui auraient pu nous venir en aide.

    Et encore : il faut aussi espérer que le log soit cohérent, et qu'il ne contiennent que les échecs véritables! Car, si tu dois, en plus, commencer par faire le tri dans ton fichier log entre ce qui n'est que log de fonctionnement et les échecs, tu n'est pas sorti de l'auberge

    Encore une fois, je comprends parfaitement ton point de vue, dans le sens où je ne joue pas les contradicteurs rien que pour me faire "l'avocat du diable". Mais je ne vois absolument rien qui puisse justifier de faire plus compliqué -- et globalement moins efficace -- pour le peu de bénéfice que l'on en tire

    Citation Envoyé par bacelar Voir le message
    Les tests (unitaires, ...) sont tous aussi présent en .Net qu'en C++.
    Bien sur que les tests sont aussi présent en .Net qu'en C++...

    Et c'est d'ailleurs le cas pour n'importe quel langage que tu pourras envisager!

    Ce qui est une raison suffisante à mon sens que pour ... partir du principe qu'une période de tests suffisante et correcte sera mise en place avant la mise en production d'un programme.

    Venez me dire que l'on a pas le temps d'appliquer cette période de tests, je vous demanderai votre carte de crédit, parce que nous perdrons plus de temps et d'argent à ne pas le faire
    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

  17. #17
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

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

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    Soyons claire, la seul justification de la non-utilisation des exceptions pour les erreurs de programmation, c'est pour respecter le mantra C/C++ : "je ne veux pas payer (en performance) pour un truc dont je n'ai pas besoin".

    Or, dans un environnement .Net, on n'est pas dans cette approche, mais plus sur : "On fait le minimum de choses relous et sans intérêt Business, quitte à avoir un coût en performance et travailler l'architecture/l'outillage pour que ce coût soit minimiser".

    Il n'y a donc, dans un environnement .Net, aucun intérêt à distinguer la phase de "test" du reste pour ces erreurs de programmation.
    Phase de test ou pas, le Framework vous dégagera.
    Un simple typage de ces exceptions pour les distinguer des exceptions "runtime" suffit à faire le distinguo.
    D'où l'intérêt de donner le maximum de détail de la boulette au développeur lors de la phase de tests ET aux mainteneurs lors de la phase de production.

    Citation Envoyé par koala01 Voir le message
    en quoi une exception pourrait-elle changer quoi que ce soit à une période de tests pourries
    Développement continu, tout même tu sais.

    Citation Envoyé par koala01 Voir le message
    Si tu as une exception en production ET qu'elle n'est pas récupérée par ailleurs pour tenter de maintenir le processus en vie aussi longtemps que possible, alors, oui, tu auras un indice du mauvais état de ta période de tests. Mais c'est tout!
    NON, les dump, les outils type AutoDumpPlus, DrWatson ou tout autre WatchDog évolué permettra de récolter bien plus qu'une simple chaine de caractère.

    Citation Envoyé par koala01 Voir le message
    Cet indice, tu l'obtiendra aussi si tu te retrouve avec un résultat totalement farfelu !
    Re NON.

    Citation Envoyé par koala01 Voir le message
    Et, si je suis d'accord avec toit que l'exception est plus "flagrante" que le résultat farfelu, ne vient pas me dire que l'exception sera plus précise pour te permettre de retrouver le problème, car ce ne sera pas forcément le cas.
    Au minimum, avec les dump, aussi précise que laisser le Framework bousillé le processus.
    L'exception n'est qu'un plus par rapport à tout le reste, cela ne supprime pas tout le reste.

    Citation Envoyé par koala01 Voir le message
    Car le seul moyen d'espérer pouvoir tirer une information cohérente à partir d'une exception, c'est qu'elle soit loguée avant d'être lancée : si on s'y intéresse au moment où on la récupère, on a déjà perdu toutes les informations de la pile d'appels, qui auraient pu nous venir en aide.
    Absolument pas !!!
    Vous collectez les informations les plus pertinentes, vous lancez l'exception, l'OS fait le dump.
    Vous avez les informations de l'exception EN PLUS de tout le reste.

    Citation Envoyé par koala01 Voir le message
    Et encore : il faut aussi espérer que le log soit cohérent, et qu'il ne contiennent que les échecs véritables! Car, si tu dois, en plus, commencer par faire le tri dans ton fichier log entre ce qui n'est que log de fonctionnement et les échecs, tu n'est pas sorti de l'auberge
    Il y a bien des framework de log sous .Net évolué, mais quel idée de log des erreurs d'exception d'erreur de programmation ?
    Exception non catch => dump bien amené.

    Citation Envoyé par koala01 Voir le message
    Encore une fois, je comprends parfaitement ton point de vue, dans le sens où je ne joue pas les contradicteurs rien que pour me faire "l'avocat du diable". Mais je ne vois absolument rien qui puisse justifier de faire plus compliqué -- et globalement moins efficace -- pour le peu de bénéfice que l'on en tire
    C'est peu si on fait nimportnawak, et le coût en terme de développement et, pour moi, négatif, car on disposent déjà de beaucoup d'Attributs .Net qui mâche la très grande majorité du travail.
    Ces Attributs .Net permettent aussi d'avoir un coût au Runtime minimisé.

    Je trouve l'approche C/C++ sur ce point bien archaïque et bien loin de la Programmation Par Aspect qui devrait gérer tout ce merdier, pour ne pas polluer notre code avec du truc sans intérêts.
    Et encore plus dans un contexte de développement continu (cycle en spirale/ Agilité).

    Je trouve que tu caricatures un peu trop .Net, @koala01.

  18. #18
    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 bacelar Voir le message
    Soyons claire, la seul justification de la non-utilisation des exceptions pour les erreurs de programmation, c'est pour respecter le mantra C/C++ : "je ne veux pas payer (en performance) pour un truc dont je n'ai pas besoin".

    Or, dans un environnement .Net, on n'est pas dans cette approche, mais plus sur : "On fait le minimum de choses relous et sans intérêt Business, quitte à avoir un coût en performance et travailler l'architecture/l'outillage pour que ce coût soit minimiser".
    Nous sommes bien d'accord qu'il y a une différence de mantra à ce sujet
    Il n'y a donc, dans un environnement .Net, aucun intérêt à distinguer la phase de "test" du reste pour ces erreurs de programmation.
    Ca, par contre, j'ai tendance à être moins d'accord...

    Ne serait-ce que parce que, selon moi, une application qui plante en production-- quelle qu'en soit la raison (comprend : qu'il s'agisse d'une erreur de logique ou d'une erreur d'input) -- ce n'est jamais bon

    A priori, la période de tests est ( sensée ) offrir un minimum de garanties quant au fait que tout se passera bien en production, parce que, si ce n'est pas le cas, les conséquences pourraient être ... fâcheuses.

    Mais je me rend compte que je prend peut-être ton intervention "à l'envers", dans le sens où tu voulais sans doute exprimer le fait qu'une période de tests "normale" ne devrait sans doute pas différer de "tant que cela" par rapport à une période de production "classique".

    J'ai beau être "un peu plus d'accord" (sur la forme) avec cette tournure de phrase, il n'empêche que j'ai été trop souvent confronté à un log (dont je ne prétend nullement qu'il était représentatif) dont la dernière entrée ressemblait à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ERROR <une date> <une heure> Uncaught exception X in main fucntion
    Bon, nous savons déjà au moins que l'application est remontée jusqu'à la fonction main... voyons plus haut ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ERROR :<un dizième de seconde plus tôt> caught exception Y in <uneclasse>::<unefonction>
    ERROR : thrown exception X in  <uneclasse>::<unefonction>
    Bon, ben, on continue à remonter dans le log ... parfois sur cinquante entrées de log (ce qui prend dix bonnes minutes), avant d'arriver à une entrée
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ERROR <une date> <une heure> Uncaught exception X in main fucntion
    qui surprend tout le monde, la concentration n'étant plus vraiment là...

    Il faut donc "un certain temps" avant de se rendre compte que ca, c'est la fin du log du bug qu'on a résolu la semaine passée. On redescend d'une entrée, et nous finissons par nous rendre compte que c'est la fonction X de la classe Machin qui a lancé une exception.

    C'est chouette, parce que l'on sait désormais où le problème a été repéré.

    On va donc dans la fonction X de la classe machin, et là, on se rend compte qu'il y a trois tests sur des préconditions, et quatre sur des post conditions.

    Avec un peu de chance, chacun de ces tests lancera une exception différente, me diras-tu.

    Sauf que deux des préconditions testées porte sur l'indice qui doit permettre d'accéder à un élément particulier, que nous irons chercher dans deux talbeaux différents (et, forcément, de tailles différentes)

    Et, bien sur -- ou serait le plaisir autrement -- ces deux pré conditions lancent exactement le même type d'exception : OutOfBound (ou quelque chose de très similaire)

    Tu me dirais que ce n'est vraiment pas de chance de rencontrer une telle situation, hein

    Ai-je déjà raconté qu'il fut un temps où l'on me surnommait "pas d'bol" Devines pourquoi!

    En attendant, la troisième pré condition lançait une exception de type différent, mais ... "pas d'bol"... : C'était bel et bien une exception de type OutOfBound que je recherchais

    C'est pas cool, parce que, pour savoir si c'est la première précondition ou la deuxième qui a tout fait foiré, il faudrait les données utilisées.

    Sauf que ces données, nous aurions pu les avoir s'il y avait eu un dump... mais que ce dump n'existe malheureusement pas (ou, du moins, il n'avait pas été transmiis, ce qui revient au même )

    Bon, ben, retour à la fonction appelante (c'était laquelle encore )

    Espérons qu'elle ait attrapé l'exception avant de la relancer -- sous une forme ou une autre -- sinon, on va s'amuser pour la retrouver

    Et, quand bien même je fini par la retrouver, parce que je suis plus têtu qu'un troupeau de mules.

    T'ai-je dit que "pas de bol" est mon surnom

    Tu en veux la preuve la voilà:

    Les deux indices transmis à la fonction qui a foiré ont été calculés par des fonctions totalement différentes, qui n'ont rien à voir l'une avec l'autre.

    Ca frise la caricature, d'autant plus que j'en parle avec humour, non

    Mais ces "quelques lignes" sont tiré d'une aventure vécue, suite à un bug qui s'était retrouvé en production, malgré une période de tests intense.

    Ce n'était peut être pas du .Net (ce qui aurait peut-être pu rendre les choses plus faciles).

    Quoi qu'il en soit, j'ai passé plus d'une heure à trouver un malheureux +1 dans une fonction dans laquelle il n'avait rien à faire. (*)

    Merci les fichiers de log!
    (*) En fait, ce n'est meme pas tout à fait vrai, car, une fois ce +1 retiré, ce sont cinq autres tests unitaires qui ont viré au rouge, parce que certaines fonction prenaient déjà ce décalage indu en compte

    Un simple typage de ces exceptions pour les distinguer des exceptions "runtime" suffit à faire le distinguo.
    D'où l'intérêt de donner le maximum de détail de la boulette au développeur lors de la phase de tests ET aux mainteneurs lors de la phase de production.
    Oui, enfin... il faut le dire vite pour ne pas mentir trop longtemps, dans certaines circonstances .

    Développement continu, tout même tu sais.
    Cela va de soi

    NON, les dump, les outils type AutoDumpPlus, DrWatson ou tout autre WatchDog évolué permettra de récolter bien plus qu'une simple chaine de caractère.
    Bien sur!!!

    A condition qu'ils tournent au moment du crash, ou, à tout le moins, que les circonstances du crash soient suffisamment précises que pour pouvoir le reproduire "fidèlement"

    Au minimum, avec les dump, aussi précise que laisser le Framework bousillé le processus.
    L'exception n'est qu'un plus par rapport à tout le reste, cela ne supprime pas tout le reste.
    Comme tu le dis si bien "avec au minimum le dump"...

    A condition de l'avoir, ce dump

    Je sais que je semble très pessimiste sur ce coup...

    Mais, "chien échaudé craint l'eau froide" dit-on... j'ai trop souvent eu à pâtir de leur absence que pour considérer leur présence comme acquise.

    Maintenant, s'il en va réellement autrement en .Net (ce qui reste du domaine des possibilités), ce sera déjà cela de pris

    Absolument pas !!!
    Vous collectez les informations les plus pertinentes, vous lancez l'exception, l'OS fait le dump.
    Vous avez les informations de l'exception EN PLUS de tout le reste.
    Pourquoi ai-je envie de rajouter un grand EN THEORIE à ton affirmation

    Je ne maîtrise pas assez .Net que pour te contredire, et je n'essaye même pas. Tout ce que je sais, c'est qu'en C++, la présence d'un dump est très loin d'être garantie.

    Encore une fois, je serait particulièrement content si elle pouvait être systématisée d'une manière ou d'une autre (et si, bien sur, le dit dump pouvait être systématiquement joint au rapport de bug )

    Si tu me garanti que c'est le cas en .Net, je te fais confiance sur ce coup

    Il y a bien des framework de log sous .Net évolué, mais quel idée de log des erreurs d'exception d'erreur de programmation ?
    Exception non catch => dump bien amené.
    N'est-ce pas

    faut dire que j'avais commencé par ouvrir un fichier de log "général" (qui loguait tout: run_time et erreurs), avant de me rendre compte de mon erreur et d'ouvrir le bon


    Je trouve que tu caricatures un peu trop .Net, @koala01.
    En fait, je ne caricature rien du tout...

    Je présente des situations malheureusement bien réelles que j'ai vécues en C++ et qui frisaient la caricature.

    Je l'ai dit et je le répète, je ne connais pas assez .Net pour prétendre que ces situations pourront survenir.

    Mais je suppute que, l'un dans l'autre, "l'intelligence" de tous les langages et de leurs différents outils reste sensiblement la même, et que, si une situation se présente dans un langage, il n'y a pas vraiment de raisons qu'elle soit absente d'un autre

    Et je n'exclus absolument pas la possibilité de me tromper, sur ce coup
    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

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Citation Envoyé par koala01 Voir le message
    en quoi une exception pourrait-elle changer quoi que ce soit à une période de tests pourries

    Si tu as une exception en production ET qu'elle n'est pas récupérée par ailleurs pour tenter de maintenir le processus en vie aussi longtemps que possible, alors, oui, tu auras un indice du mauvais état de ta période de tests. Mais c'est tout!

    Cet indice, tu l'obtiendra aussi si tu te retrouve avec un résultat totalement farfelu !

    Et, si je suis d'accord avec toit que l'exception est plus "flagrante" que le résultat farfelu, ne vient pas me dire que l'exception sera plus précise pour te permettre de retrouver le problème, car ce ne sera pas forcément le cas.

    Car le seul moyen d'espérer pouvoir tirer une information cohérente à partir d'une exception, c'est qu'elle soit loguée avant d'être lancée : si on s'y intéresse au moment où on la récupère, on a déjà perdu toutes les informations de la pile d'appels, qui auraient pu nous venir en aide.
    Ce n'est pas seulement une question d'information contenue dans l'exception, mais que dans mon exemple (bounds-checking avec exception), tu as l'exception dès que tu tentes d'écrire hors du tableau, plutôt que d'attendre le moment où l'objet affecté par ton écriture invalide est utilisé.
    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.

  20. #20
    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 Médinoc Voir le message
    Ce n'est pas seulement une question d'information contenue dans l'exception, mais que dans mon exemple (bounds-checking avec exception), tu as l'exception dès que tu tentes d'écrire hors du tableau, plutôt que d'attendre le moment où l'objet affecté par ton écriture invalide est utilisé.
    Et que fait, selon toi, une assertion, si ce n'est pas de s'assurer que tu n'essayes pas d'accéder à un élément hors limites

    Je comprends très bien ce que tu me dis : le problème de l'assertion, c'est qu'elle est purement et simplement absente du code de production. Soit. Je ne peux qu'être d'accord avec toi sur ce point.

    Mais je ne vois malgré tout pas en quoi cela pourrait être un problème, à partir du moment où les situations qui pourraient dégénérer sont -- en tout état de cause -- sensées avoir été corrigées.

    Car, l'un dans l'autre, c'est ce que l'on veut et ce dont il faut pouvoir s'assurer : que les erreurs de logiques qui pourraient mener à un accès hors limite soient interceptées le plus tôt possible et, idéalement corrigées.

    Or, il n'y a rien à faire, une fois que la logique est correcte (et donc, dans l'exemple qui nous occupe : qu'elle s'assure de ne pas autoriser d'accès hors limite), il n'y a absolument aucune raison pour qu la situation qui pourrait poser problème puisse survenir!

    Si tu as une fonction foo qui vérifie -- quelle que soit la manière envisagée -- que la valeur de index est dans les limites admises d'un tableau, elle peut être appelée par vingt fonctions différentes, il n'en demeure pas moins que ces vingt fonctions sont sensées avoir été testées et que, quoi qu'il arrive, si elle permettaient à index de représenter une valeur hors limite, il est "logique" de se dire que l'erreur aura été repérée et signalée.

    Et donc, comme l'exécution ne pouvait purement et simplement pas continuer tant que la logique qui mène à l'appel de foo n'était pas corrigée, il est plus que cohérent de partir sur le principe que... la logique a été corrigée.

    Or, si la logique a été corrigée, il n'y a plus aucune raison pour qu'une situation dans laquelle un accès hors limites ne survienne en production!

    Et, à partir du moment où il n'y a plus aucune raison pour que le problème d'accès hors limite survienne en production, la présence même du test qui vérifie la valeur de index et qui prend "toutes les mesures adéquates" n'a plus la moindre raison d'être.

    Mettons nous bien d'accord : je comprends parfaitement les raisons de la programmation défensive, et j'y adhère, à tout le moins, dans toutes les situations qui risquent d'échapper au contrôle (et à la bonne volonté) du développeur.

    Je n'ai donc aucun problème à envisager de lancer une exception si, par exemple (liste non exhaustive, cela va de soi)
    • le système refuse de me fournir la mémoire dont j'ai besoin
    • le système me refuse l'accès à un fichier
    • je dois m'adresser à un système distant qui ne répond pas
    • je dois m'adresser à un système distant qui me refuse l'accès
    • et bien d'autres encore


    Mais je juge la programmation défensive particulièrement inadaptée lorsqu'elle sert à mettre en évidence des problèmes liés à la logique même du programme.

    Parce que, justement, lorsqu'elle sert à mettre en évidence des problèmes liés à la logique du programme, les situations qu'elle met en évidence n'échappent absolument pas au contrôle (et à la bonne volonté) du développeur.
    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.
Page 1 sur 2 12 DernièreDernière

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