Journal De l'influence néfaste de Google sur les développeurs C++

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes :
43
6
août
2022

Aujourd'hui, journal qui dénonce grave.

Je voudrais m'élever, non, m'insurger contre cette fascination morbide d'un certain nombre de professionnels pour la firme de Larry Page et Sergey Brin. Une sorte de biais cognitif qui consiste à penser que "Google est prospère, Google fait ça, alors si je fais ça moi aussi je serai prospère". Et Google, qui adore faire la leçon au reste du monde, comme pour s'auto-convaincre que leur réussite ne doit qu'à l'intelligence et la méthode, et surtout pas à un gigantesque coup de bol, non monsieur, se fait un plaisir de répandre la doxa.

Et donc, Google a publié il y a de cela de nombreuses années un guide du C++, très limitant, en particulier sur l'héritage multiple (avec une vision très Java d'héritage simple et d'interfaces multiples) et les exceptions, dont ils disent pis que pendre. Inefficaces, peu lisibles, un Goto moderne. Le chien de Larry a du se faire mordre par une exception quand il était petit, c'est pas possible autrement. Dans la version courante du document, il semblerait que les têtes pensantes de chez Google ont mis un peu d'eau dans leur vin: les exceptions ne sont toujours pas autorisées, mais ils conviennent que c'est plutôt pour des raisons historiques et que si c'était à refaire, bref. N'empêche que le mal est fait, et nombre de middle-managers pressés voulant gonfler leur égo en réunion vont lire la première phrase, "nous n'utilisons pas les exceptions", et ressortir ça à toutes les sauces. Et je vois maintenant de nombreux candidats à l'embauche, jeunes et moins jeunes, qui sont terrifiés par les exceptions.

Si Google ne veut pas utiliser d'exceptions, grand bien leur fasse. Ils peuvent même coder avec une main dans le dos et une pieuvre sur la tête si ça leur fait plaisir. Mais ce qui est bon pour Google ne l'est pas forcément pour d'autres. Mieux, je considère que dans la vaste majorité des cas, si c'est bon pour Google, alors c'est plutôt mauvais pour moi. Lisez, réfléchissez, tentez de comprendre et de justifier avant de vous précipiter une technologie ou un standard parce que Google a dit (s'applique aussi à Facebook, Microsoft, Amazon, et la voisine du 5ème).

Google s'est posé en prosélyte, et ces développeurs influençables s'en font les exégètes, et se persuadent que les exceptions, c'est mal, et que ce que dit Google, c'est bien, avec au sein de leur inconscient le secret l'espoir de devenir à leur tour l'équivalent d'un Google millionnaire et de posséder eux aussi leur gros yacht bien polluant sur lequel ils passeront précocement de vie à trépas par le truchement d'une demi-mondaine et d'une seringue d'héroine..

Et donc, en entretien, je propose au candidat d'écrire une petite fonction, oh, quelque chose de tout bête, qui prend un conteneur quelconque et renvoie le plus grand élément du conteneur. La bonne nouvelle, c'est que la majorité des candidats se rendent compte vite fait qu'il faut faire quelque chose de particulier quand le conteneur est vide. La mauvaise nouvelle, c'est qu'ils sont trop nombreux à murmurer "exception", puis, comme effrayés de leur propre témérité, à rétropédaler et tenter de trouver par tous les moyens une alternative, alors on va passer par référence et renvoyer un booléen, oh non peut-être un pointeur? Ou alors un optionnel. Même les pros s'y mettent: regardez moi donc chez Apache Arrow, une bibliothèque qui propose un format plutôt bien fichu d'ailleurs pour faire du transfert ou de l'analyse de données. Et bien, ces gens, leurs types de retour sont systématiquement un "Result", et ils fournissent des macros pour s'en dépatouiller, qui vont renvoyer Machin si le résultat est correct, en lancer une exception si il ne l'est pas. Regardez moi ça, c'est moche, c'est inefficace. Mais c'est compatible Google, et c'est l'essentiel.

Alors oui, les exceptions ne sont pas l'alpha et l'oméga, et il y a de nombreuses situations où elles ne sont pas pertinentes. Mais dans mes systèmes financiers, là où on va prendre une donnée, l'enrichir avec quelques appels à une base de données, y rajouter deux trois fichiers, faire mouliner du code de divers paquets fournissant analyses diverses, pour aller sauver le résultat dans un autre fichier, si mes paramètres d'entrée sont mauvais, mon appel à la base foire, ou le disque plein, je ne vais pas "traiter l'erreur là où elle est lancée", et relancer la base ou faire de la place sur le disque depuis mon code. Non, je lance une exception, laquelle va remonter les 30 ou 50 niveaux dans ma pile, je l'attrape presque en haut, je logge, je fais remonter au système d'alerte, et je passe à l’élément suivant.

Alors on explique, on discute, on forme, on justifie. Mais mon calme et ma bonhommie légendaires sont soumis à rude épreuve quand on me rétorque que "Mais regarde, Google le fait !".

  • # Une comparaison musicale

    Posté par  (site web personnel, Mastodon) . Évalué à 3.

    Pour certaines raisons, le ton de ce journal me fait penser au titre Institutionalized, dans sa version de Body Count.

    La connaissance libre : https://zestedesavoir.com

  • # Argument d'autorité

    Posté par  . Évalué à 5.

    L'argument d'autorité est tendance en ce moment.

    • [^] # Re: Argument d'autorité

      Posté par  (site web personnel) . Évalué à 2.

      Sauf que cela date de bien avant l'existence de Google cette pratique, et qu'elle est totalement justifiée.

      Ça froisse peut-être les adorateurs de stroutproute.. Désolé pour eux.

      • [^] # Re: Argument d'autorité

        Posté par  (site web personnel) . Évalué à 7.

        Je me souviens effectivement d'avoir lu ceci en 2003 : https://www.joelonsoftware.com/2003/10/13/13/
        Bon, Google existait déjà. Je sais pas ce que le coding style Google C++ de l'époque disait mais je suis à peu près sûr qu'approximativement personne dans le vrai monde n'en n'avait entendu parler alors.

        pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

    • [^] # Re: Argument d'autorité

      Posté par  . Évalué à 10.

      La frilosité du middle management et des salariés dans une grande orga. Ça joue aussi.

      Tu peux faire autant de merde que tu veux, tant que t’as suivi la procédure, la règle (explicite ou pas), ça passe crème.

      Tu sors des sentiers battus, même si ça fonctionne (surtout!), et là le retour de bâton…

      Mort aux cons !

      • [^] # Re: Argument d'autorité

        Posté par  . Évalué à 0.

        Je serais pas autant vindicatif que ça.
        Dans les corps, il y a 3 points :
        - savoir faire
        - savoir être
        -process
        Il suffit d'être bon dans deux domaines et pas nul dans ton point faible et tu t'en sors.

        • [^] # Re: Argument d'autorité

          Posté par  . Évalué à -6.

          s/savoir être/lécher les couilles de ton supérieur/g

          C’était le traducteur automatique novlangue managériale -> français.

          Mort aux cons !

          • [^] # Re: Argument d'autorité

            Posté par  . Évalué à 5.

            Je pensais plutôt , bonjour, merci etc…

          • [^] # Re: Argument d'autorité

            Posté par  (site web personnel, Mastodon) . Évalué à 1. Dernière modification le 08 août 2022 à 10:48.

            Le supérieur est donc forcément un homme ?

            « Tak ne veut pas quʼon pense à lui, il veut quʼon pense », Terry Pratchett, Déraillé.

            • [^] # Re: Argument d'autorité

              Posté par  . Évalué à 4.

              En informatique…

              Il s’avère que les femmes sous lesquelles j’ai bossé avaient bien moins de complexe de supériorité. Donc ouai, bien moins besoin de faire de la lèche pour être reconnu à sa juste valeur. C’est même flagrant.

              Le cas que je connais présentement est caricatural : même boîte, même équipe, tout à l’identique. La manageuse part en retraite, le manageur qui la remplace arrive. Du jour au lendemain, plus de projets, je suis limite au placard. Et je sais pas m’y prendre avec les supérieurs, le concept d’autorité m’est totalement étranger. Il m’a testé d’ailleurs, il m’a fait un coup de pute (vouloir me faire travailler un dimanche, en prévenant seulement une semaine à l’avance, via un mec qui n’est pas du tout dans la chaîne hiérarchique — mais qui est copain avec le dit manager, ce que j’ignorais bien sûr…). Alors qu’avec la précédente il m’est arrivé de m’engueuler… (c’était quelqu’un de très autoritaire).

              Mort aux cons !

      • [^] # Re: Argument d'autorité

        Posté par  . Évalué à -1.

        Je ne parlerai pas d'argument d'autorité, Google n'ayant aucune autorité sur notre code en sois.

        C'est plus un argument de "Google à les meilleurs ingénieurs, ils sortent des super-trucs alors tout ce qu'ils font est super.". En fait il est difficile de remettre en question quelqu'un qui a souvent raison… et quand il se plante (pour des raisons bête souvent), tout le monde le suit par méconnaissance (manque de raisonnement).

        Il faut quand même reconnaître que leur moteur de recherche à toujours été bien meilleurs que les autres, que leurs protocoles sont top, que leur langage Go a quelque chose de génial… Pour autant Rust à bien plus réussi le but du langage Go (Go a trouvé un autre intérêt mais ce n'est pas pour ça qu'il à été conçu).
        C'est peut-être la le secret de Google, on lance plein de truc, et par sélection naturel, on regarde ce que ça donne et on garde ou pas suivant le résultat… Mais parfois…

        • [^] # Re: Argument d'autorité

          Posté par  . Évalué à 3.

          C'est plus un argument de "Google à les meilleurs ingénieurs, ils sortent des super-trucs alors tout ce qu'ils font est super.".

          Donc tu reconnais à ces ingénieurs autorité en la matière sous le seul prétexte qu'ils sont employés google. C'est bien un argument d'autorité.

          En fait il est difficile de remettre en question quelqu'un qui a souvent raison…

          C'est rarement une question de vérité ou d'erreur, mais des choix. Google dans leur contexte font des choix, tu n'a probablement pas le même contexte peut-être que tu pourrais faire d'autres choix.

          et quand il se plante (pour des raisons bête souvent), tout le monde le suit par méconnaissance (manque de raisonnement).

          On appel ça le cargo cult.

          Pour autant Rust à bien plus réussi le but du langage Go (Go a trouvé un autre intérêt mais ce n'est pas pour ça qu'il à été conçu).

          Ils n'ont pas le même objectif. Ils ne jouent pas dans le même bac à sable.

          C'est peut-être la le secret de Google, on lance plein de truc, et par sélection naturel, on regarde ce que ça donne et on garde ou pas suivant le résultat… Mais parfois…

          Oui et non la sélection naturelle en question n'est que partiellement lié à la qualité intrinsèque du sujet. Le fait d'avoir réussi à le populariser ou à le vendre joue beaucoup, le fait que le gars qui est à la tête sache plus ou moins jouer des coudes aussi… Il y a bien sûr une part non négligeable d' aléatoire.
          Ensuite il ne faut pas voir une grande entreprise comme google comme un monolithe dont tous les employés seraient aussi compétents les uns que les autres.

          https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

          • [^] # Re: Argument d'autorité

            Posté par  . Évalué à 3.

            Donc tu reconnais à ses ingénieurs autorité en la matière sous le seul prétexte qu'ils sont employés google. C'est bien un argument d'autorité.

            Ce n'est pas une autorité que je leur reconnais. Mais tu as beaucoup plus envie de suivre l'entreprise qui réussi, qui a les compétences que l'entreprise qui fait faillite même si ton cas se rapproche de l'entreprise qui fait faillite (et c'est pareil pour l'entreprise qui semble réussir mais qui est inconnu…). Ce n'est pas une question d'autorité, c'est une question de libre arbitre. Ton libre arbitre te fais faire des choix. Et en l'absence de connaissance infini, il est difficile de faire toujours les meilleurs choix, il est souvent plus raisonnable de suivre le troupeau/l'entreprise leader. Je ne dis pas que ce soit bien ou quoi mais je dis que cela se comprends bien (et statistiquement cela te donne plus de chance de survivre…).

            Ils n'ont pas le même objectif. Ils ne jouent pas dans le même bac à sable.

            Aujourd'hui ils n'ont pas les même objectifs, mais a l'origine si : remplacer le C, faire un langage performant utilisable pour écrire des OS. Rust à réussi son objectif. Go l'a manqué mais s'est réorienté vers un objectif un peu différent qui est la productivité pour des performances élevé (sans équivaloir C). Cela conviens à Google et à bien d'autres.

            • [^] # Re: Argument d'autorité

              Posté par  . Évalué à 4.

              Ce n'est pas une autorité que je leur reconnais.

              Quand on parle d'argument d'autorité on pense à :

              Personne qui jouit d'une grande considération, dont on invoque l'exemple à l'appui d'une thèse

              CNRTL : Autorité B > 1 > a

              Tu peux trouver une description plus poussée (bien qu'un peu complexe à lire) dans le 30ème stratagème de l'art d'avoir toujours raison.

              Et oui ce sont 2 arguments d'autorité.

              Mais il ne faut pas se focaliser sur le mot autorité le fait que tu reconnaisse ou pas cette définition ne change pas le fond.

              On parle de singer une entreprise parce que cette entreprise semble avoir du succès (tout chez google ne rencontre pas de succès, coucou google cloud et ces 3 milliards de déficit l'an dernier). En quoi est-ce que tu relis le succès de google à leur utilisation de C++ (bon ils utilisent aussi js, python, go, dart, java, kotlin, typescript, PHP, groovy,…) plutôt qu'au fait qu'ils soient installés à Mountain View ou qu'ils utilisent un dépôt de code unique ou qu'ils ont un nom en 6 lettres ? Facebook et Alibaba se portent bien et utilisent principalement PHP pourquoi choisir google ? Microsoft lui utilise C# c'est que ça doit être bien aussi ?…

              Tu peux choisir le langage que tu veux et choisir par rapport à ça l'entreprise iconique qui va te permettre de dire que ton choix et vachement bien. Ça montre la faiblesse de l'argument à mon avis.

              Ça ne dit pas que ton choix est mauvais. On fait tous des choix par argument d'autorité, mais pour autant c'est utile d'en avoir conscience.

              Aujourd'hui ils n'ont pas les même objectifs, mais a l'origine si : remplacer le C, faire un langage performant utilisable pour écrire des OS. Rust à réussi son objectif. Go l'a manqué mais s'est réorienté vers un objectif un peu différent qui est la productivité pour des performances élevé (sans équivaloir C). Cela conviens à Google et à bien d'autres.

              Alors l'annonce de google de 2009 donc pour annoncer la première version publique de go avant la version 1 et indique :

              Go combines the development speed of working in a dynamic language like Python with the performance and safety of a compiled language like C or C++.

              Donc ce n'est pas un mouvement après coup c'était l'objectif initial. Il est question de programmation système et le simple succès que go a dans l'environnement docker/podman/kubernetes/helm/gvisor montre que ça fonctionne (et ça n'est pas une erreur : podman a démarré en 2018 donc après l'arrivée de rust et helm a était réécrit il y a 2 ans).

              https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

              • [^] # Re: Argument d'autorité

                Posté par  (site web personnel) . Évalué à 0.

                On parle de singer une entreprise parce que cette entreprise semble avoir du succès

                L'argument d'autorité est plutôt lié au fait qu'ils ont un objectif de rentabilité qui colle avec ce que chacun cherche, et qu'ils sont gros donc les guidelines ont un impact financier (en dépenses) immédiat donc qu'ils ne disent pas ça pour le fun contrairement au perdu dans son coin.

                Alors avant de faire son "anti" juste par plaisir, il faut comprendre qu'il faut amener un bon argument devant les gens pour dire que Google se trompe et/ou que ce n'est pas adapté à celui qui te lit. Le journal critique mais (il me semble) ne tombe pas dans ce piège.

                Dire "c'est un argument d'autorité donc c'est négatif" (sous-entendu du premier commentaire du thread) n'est pas un argument.

                • [^] # Re: Argument d'autorité

                  Posté par  . Évalué à 3.

                  Dire "c'est un argument d'autorité donc c'est négatif" (sous-entendu du premier commentaire du thread) n'est pas un argument.

                  Si c'est ce qui est compris par le premier commentaire, il est explicite dans mon dernier que je dis simplement que l'argument est faible. Je discute les arguments, je n'ai dis ni sous entendu nul part ce qu'il faut faire ou pas. Mais je trouve surprenant qu'en disant ça :

                  C'est rarement une question de vérité ou d'erreur, mais des choix. Google dans leur contexte font des choix, tu n'a probablement pas le même contexte peut-être que tu pourrais faire d'autres choix.

                  Tu me vois affirmer ce qui est bon ou pas.

                  Je présume que tu considère qu'affirmer qu'un argument est d'autorité serait une disqualification alors que pas du tout.

                  Alors avant de faire son "anti" juste par plaisir, il faut comprendre qu'il faut amener un bon argument devant les gens pour dire que Google se trompe et/ou que ce n'est pas adapté à celui qui te lit.

                  Première chose je n'ai jamais dis que Google se trompait, mais que s'ils ont fait ces choix d'autres sont possibles et je ne l'ai dit qu'une seule fois.

                  Secondement il s'agit juste de comprendre nos choix et pourquoi on les fait. Il y a pleins de situations où nous utilisons nos biais, des simplifications et des arguments plus ou moins solides, c'est normal. Mais ça me parait utile d'en être conscient quand il s'agit de ton métier.

                  https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

  • # Qt

    Posté par  (site web personnel) . Évalué à 7.

    Qt est encore plus stricte. Les vrais dev n'utilise pas d'exception en C++, ni de rtti. C'est un problème d'implémentation par les compilateurs. Pas un problème lié au langage. Je ne te fais pas la leçon.

    • [^] # Re: Qt

      Posté par  . Évalué à 8.

      Du coup ceux qui ne font pas de C++ peuvent-ils avoir une explication moins sibylline ?

      Mort aux cons !

      • [^] # Re: Qt

        Posté par  (site web personnel) . Évalué à 10. Dernière modification le 06 août 2022 à 13:57.

        De mon expérience chez ESI Group, Dassault System et Sun Microsystems (ça date, mais le but était d'optimiser au max …):

        • Taille du binaire bien plus importante (peut-être problématique sur les gros programmes genre CATIA, car l'augmentation de taille n'a pas d'impacte sur de petits programmes) ;
        • Le compilateur est coincé pour certaines optimisations (chez Dassault, on déconseillait les returns, il n'en fallait qu'un par méthode) ;
        • Très mauvais d'abuser des exceptions dans un cadre multi-threadé (voir C++ exceptions are becoming more and more problematic) ;
        • Grandes disparités dans la façon de gérer les exceptions en fonction de la plateforme ;

        Je ne veux pas dire de bêtises, j'ajouterai la compatibilité binaire (snif) encore plus complexe à maintenir sur les très très gros projets, où l'on n'a pas accès à toutes les sources et où l'on ne compile jamais tout sur 1 seule machine.

        • [^] # Re: Qt

          Posté par  (Mastodon) . Évalué à 3.

          les très très gros projets, où l'on n'a pas accès à toutes les sources et où l'on ne compile jamais tout sur 1 seule machine

          T'aurais un exemple ? Pour avoir déjà compilé un système Android complet en 15mn sur un PC (de guerre, certes), je suis étonné que "de nos jours" ça existe encore.

          En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.

          • [^] # Re: Qt

            Posté par  (site web personnel) . Évalué à 10.

            C'était il y a longtemps, Android c'est Linux (du C), avec un Desktop et un runtime en C. Il doit y avoir du C++, mais ce n'est pas la majorité du code.

            Je pense par exemple que Freecad est plus long à compiler (ou Chromium, voir KDE). Moi je parlais de CATIA V4 et V5, DELMIA, Enovia, avec les test-cases, et c'était il y a plus de 15 ans (mais sur des machines sur-puissantes, avec 8 CPU)… Il y avait 85 000 binaires à recompiler et à linker … En plus de cela, certains outils utilisé par des gros client (PSA, Renault, WV, Ford, Airbus ..) étaient développés par dessus CATIA, et les clients n'avaient pas nécessairement les sources. Par contre, ça doit linker, ton compilateur et les options que tu choisis doivent ne pas changer, ou ne pas modifier l'ABI. Il y avait des dépendances cycliques (à la Rust, pas de formward déclaration en Rust), qui t'obligeait à linker 2 fois.

            C'était le panard, j'ai pris mon pied à faire de l'optimisation CPU et GPU, avec un CPU moins puissant, on éclatait Intel, IBM and co. Bref, c'était tout une époque.. Je voulais m'occuper du portage Linux, je ne sais pas ou s'en est. Billou à donné un très TRÈS gros chèque pour nous faire partir…

          • [^] # Re: Qt

            Posté par  (site web personnel) . Évalué à 3.

            Juste pour te donner un ordre d'idée, nous optimisions la façon dont les bibliothèques étaient chargées, et à froid, sans charger de modèle, CATIA V5 c'est 1,6 Gio dans la RAM. Je ne connais pas de logiciel plus conséquent… en 2005, en 32-bit.

        • [^] # Re: Qt

          Posté par  (site web personnel) . Évalué à 10.

          Ce n'est pas parce que les exceptions posent problèmes avec certains types de projets qu'il faut les bannir de tous les projets.

          Tu fais part de ton expérience, que je ne remets aucunement en question, pour justifier ta position sur les exceptions. Du coup, je vais te faire part de la mienne.

          J"ai travaillé sur des projets assez conséquents, bien que probablement sans commune mesure avec ceux des organismes que tu cites. Il n'en reste pas moins que, pour ma part, les exceptions ne m'ont jamais, au grand jamais, posé de problèmes, même en ce qui concerne la taille des exécutable ou leurs performances.
          Dans le cas notamment des daemon, des programmes qui sont censés être particulièrement fiables, je trouve personnellement que ce sont les exceptions (ou mécanisme similaire) qui permettent le plus facilement d'assurer cette fiabilité.

          Bref, il en va des exceptions comme de toutes les technologies : il faut les utiliser à bon escient ; certaines projets s’accommodent parfaitement de leurs inconvénients, d'autres non…
          Par conséquent, je n'ai aucun scrupule à les utiliser dans mes logiciels, et j'écris ceci tout en reconnaissant qu'elles ne sont pas exemptes de défauts…

          Cyberdépendance, cyberharcèlement, pédocriminalité… : Zelbinium, pour que les smartphones soient la solution, pas le problème !

        • [^] # Re: Qt

          Posté par  (site web personnel) . Évalué à 3.

          Pas de bon support d'annotations des fonctions pour lister les exceptions qui peuvent être retournées et garantir qu'elles soient toutes soit attrapées soit transférées.

    • [^] # Re: Qt

      Posté par  (site web personnel) . Évalué à 2.

      Dommage que Qt ne soit pas aussi strict sur la rétrocompatibilité.

      Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

      • [^] # Re: Qt

        Posté par  . Évalué à 1.

        Ils sont peut être trop occupé pour s'occuper des clients qui ne mettent pas à jour, par exemple corriger les bugs de l'actuelle version

        • [^] # Re: Qt

          Posté par  . Évalué à 1.

          En meme temps, quand tu vois le passage de Qt5 a Qt6, des modules de base Qt5 ne sont toujours pas disponible dans Qt6.3 … Tout ce qui tourne autour d Android, c'est une cata.

  • # Pas de fumée sans feu

    Posté par  . Évalué à 10.

    Il y a sûrement des scénarios où une exception peut faire l'affaire. Mais personnellement, je trouve que le nombre de pièges que ça pose éclipse le gain en lisibilité.

    En fait, ce sont essentiellement les mêmes pièges que goto: on perd la maîtrise du flux de la fonction.

    Dans ton exemple, une exception est lancée 50 niveau en dessous. Il faut donc, systématiquement, et à chaque étage:

    • s'assurer qu'il n'y a aucune fuite mémoire
    • s'assurer qu'une transaction commencée sera annulée et/ou close

    Tout ça, sachant que potentiellement sur 48 niveaux il n'y a aucun indice que l'appel de fonction lèvera une exception en plein milieu de l'algorithme…

    Alors apparemment ça semble adapté à ton cas, tant mieux pour toi. Mais je reste attaché à ce que mes fonctions s'exécutent jusqu'à leur unique point de sortie, avec bien sûr une gestion des échecs d'appels aux sous-fonctions.

    • [^] # Re: Pas de fumée sans feu

      Posté par  (site web personnel) . Évalué à 8.

      Alors apparemment ça semble adapté à ton cas, tant mieux pour toi.

      A noter que dans l'exemple fourni, le standard C++ dit que std::max_element(first, last) retourne last (qui est exclu du range donc différentiable des valeurs légitimes) si le conteneur est vide, et ne fait donc pas d'exception dans ce cas.

      Je n'arrive toujours pas à me décider si les exceptions sont bien ou pas dans cette gueguerre binaire, entre ceux n'en voulant jamais de jamais et ceux faisant des benchs jusqu'à 10% des appels en exception, et avec un minimum de 0.1% (perso je ne trouve pas 0.1% comme exceptionnel mais faisant partie de la vie du code), cf lien dans un commentaire plus haut, mais j'ai la plus nette impression qu'on devrait utiliser les exceptions pour des cas… exceptionnels, genre plus de RAM, plus de disque, plus de réseau, etc… Et pas dans le cas d'un conteneur vide si qu'il soit vide est normal.

      • [^] # Re: Pas de fumée sans feu

        Posté par  (site web personnel) . Évalué à 9.

        j'ai la plus nette impression qu'on devrait utiliser les exceptions pour des cas… exceptionnels, genre plus de RAM, plus de disque, plus de réseau, etc… Et pas dans le cas d'un conteneur vide si qu'il soit vide est normal.

        Je le crois aussi. Le find d'ocaml lance une exception quand il ne trouvais pas une valeur dans une list. C'était le seul cas de "catch". Pour moi l'exception est un moyen plus propre "de remonter jusqu'en haut", sans avoir une chaine de err à réincapsuler comme dans go (c'est simple à comprendre, mais c'est moche avec un "snr" pas top).

        Pour tout ce que l'on ne peut pas gérer et qui finirait par un exit (db morte, µservice qui ne répond pas, fichier absent qui est censé être toujours là), c'est plus simple de remonter en haut du code, qui décide de faire un reset, un suicide (pour être relancer par un moniteur), ou juste un log. Bien sûr, il faut éviter de faire ça au milieu de quelques choses qui ressemblent à une transaction.

        "La première sécurité est la liberté"

      • [^] # Re: Pas de fumée sans feu

        Posté par  . Évalué à 5.

        entre ceux n'en voulant jamais de jamais et ceux faisant des benchs jusqu'à 10% des appels en exception

        C'est effectivement binaire, mais je pense (en dehors du fait que je n'aime pas trop les exceptions) qu'il n'y a pas 42 façons d'utiliser les exceptions. En gros:

        • pas du tout
        • juste le strict minimum, en gros l'appli devient quasi inutilisable et on «plante élégamment»
        • partout, comme système de gestion d'erreur par défaut

        Faire un entre-deux est probablement le pire: on ne sait jamais si on doit gérer ou pas les exceptions, et du coup c'est géré de façon bancale.

      • [^] # Re: Pas de fumée sans feu

        Posté par  (site web personnel) . Évalué à 6.

        L'approche par itérateurs de la bibliothèque standard permet de gérer très élégamment le problème des cas particuliers, c'est impeccable et permet effectivement un grand contrôle: lancer une exception, retourner un optionnel, renvoyer une valeur par défaut…

        Mais dans ma question d'entretien, l'on demande expressément à renvoyer "le plus grand élément", pas un itérateur. Or, mathématiquement, le plus grand élément d'un ensemble vide, ça n'a pas de sens. Donc, renvoyer une exception est raisonnable.

        Renvoyer un optionnel, par exemple, va m'empêcher de chaîner les appels, et va me forcer à écrire beaucoup de boilerplate moche, à moins de n'avoir que des optionnels partout (bonjour la complexité pour vérifier chaque optionnel au début de chaque fonction, sans compter l'impact sur les perfs).

        Maintenant, le candidat qui me dit que cela dépend de la manière dont l'équipe a décidé de gérer les erreurs, et qui peut vraiment comparer les avantages et les inconvénients des différentes méthodes, je suis ravi ! C'est quelqu'un qui réfléchit, et qui s'adapte au contexte du problème.

        Je te rejoins sur cette histoire de 0.1% qui me semble bien trop élevé. Je fais régulièrement tourner gdb avec un "catch throw" dans nos binaires, et je vois bien que dans les faits, quand tout se passe bien, l'on ne jette pas une seule exception, ou alors à la rigueur à l'initialisation parce qu'il y a toujours des choses à jeter dans nos jeux de données. En revanche, quand ça se passe mal, on nettoie tout bien, on a de belles erreurs à peu près claires, et l'on peut continuer le traitement.

        • [^] # Re: Pas de fumée sans feu

          Posté par  . Évalué à 5.

          sans compter l'impact sur les perfs

          Il me semblait que le c++ pouvait faire des optional/either en zero cost abstraction ?

          https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

          • [^] # Re: Pas de fumée sans feu

            Posté par  (site web personnel) . Évalué à 4.

            Ce que je comprends du zero cost abstraction, c'est que ce n'est pas plus cher que de passer le type plus le booléen, ce qui est déjà vachement bien. Mais il faut quand même passer le booléen, donc à moins d'inliner, il y a un coût. Méfiance également car c'est un coup à faire des copies là où des move seraient passés crème.

            Après, c'est un type extrêmement bien fichu, et j'ai dignement fêté son arrivée dans C++17 (en remplaçant partout dans dans mon code son ancêtre de chez Boost).

        • [^] # Re: Pas de fumée sans feu

          Posté par  . Évalué à 5.

          Renvoyer un optionnel, par exemple, va m'empêcher de chaîner les appels, et va me forcer à écrire beaucoup de boilerplate moche, à moins de n'avoir que des optionnels partout (bonjour la complexité pour vérifier chaque optionnel au début de chaque fonction, sans compter l'impact sur les perfs).

          Heu, ouais, mais si t’as pas d’élément le plus grand, il va bien falloir gérer ça quelque part quand même. Soit tu gères ça au call site (voir le call site du call site), soit tu laisses ça remonter en mode « quelqu’un d’autre peut pas l’faire?!? ».

          Dans le premier cas, t’es pas dans une semantique d’exception, mais de gestion de code d’erreur. Le compilateur peut t’aider si le langage a un support avancé de ce genre de choses, mais au final, la problématique est surtout « cosmétique »: comment écrire le code de facon concise et lisible. Utiliser des exceptions parce que ça te permet de try/catcher au call site plutôt qu’un if return, c’est un peu bourrin. Et surtout, ça va pas fondamentalement résoudre le problème si le call site risque de se prendre plus d’une exception. Même topo si tes call stacks sont très profondes: t’as un très probablement un problème architectural. Si tu viens me dire qu’un if/return est trop cher, ma réponse va être: j’en doute, en tout cas pas sans un benchmark sérieux montrant un impact concret (pas un benchmark qui cherry pick « 50% mieux », en omettant de dire que les 50% mieux, c’est 2ms, ou 0.1% du temps d’exécution total).

          Dans le deuxième cas, c’est très précisément ce qu’il ne faut PAS faire avec des exceptions. Les laisser remonter dans une couche qui n’est même pas au courant que les couches inférieures existent.

          Linuxfr, le portail francais du logiciel libre et du neo nazisme.

      • [^] # Re: Pas de fumée sans feu

        Posté par  . Évalué à 10.

        Comme tu dit, les exceptions doivent être exceptionnelles. Ne pas trouver un élément dans une collection n’est pas exceptionel, c’est attendu, et ça doit absolument être géré au call site. Pour être tout à fait honnête, pour du code métier, c’est très dur de trouver un exemple ou les exceptions sont justifiées. Ton code métier, il a des cas d’erreurs, et ils sont spécifiés clairement (ou devraient l’être, à l’ingénieur de bosser avec ses stakeholder quand il en découvre). Les erreurs techniques, dans leur immense majorité, elles sont plutôt predictibles, et se ramènent très souvent à une erreur métier. Ta db est en carafe? Est ce que c’est si différent que ne pas trouver ton élément? Probablement pas.
        Ton dao/repository/whathaveyou, lui va probablement vouloir faire la différence entre « pas trouvé » et « bestel il a pété la db et elle a fait pfft », pour ton logging/monitoring notamment. Mais la encore, y’a des façons beaucoup plus élégante de représenter ca. Tu reçoit soit le résultat, soit une erreur. Ça se modèle très bien avec un tuple, et des enums vont rendre le code de gestions d’erreur beaucoup plus simple à écrire parce que ca se découvre tout seul.

        Je ne mettrais pas plus de disque dans un cas exceptionel non plus. Les I/O ça peut foirer pour un paquet de raisons tout à fait légitimes et courantes. Idem pour le réseau. C’est pas vraiment un cas exceptionel, et si le call site par du principe que l’io va passer, ben le code est très fondamentalement buggé (et a probablement des problèmes plus gros que de savoir s’il faut lancer une exception).

        Plus de ram, je mettrais ça effectivement dans une exception. Essentiellement parce que si t’as plus de ram, c’est potentiellement très difficile de s’en sortir, vu que tu risques d’avoir besoin de ram pour libérer de la ram. Disons que c’est un peu la branche sur laquelle t’es assise, si elle pete, ça devient tout de suis plus compliqué.

        Bref, de mon point de vue, les exceptions posent 2 gros problèmes:
        - perte complète du flow de contrôle ou tu vas de la à de la d’un coup sans crier gare
        - problèmes des performance lié au stack unwinding etc

        Le deuxième point s’applique surtout à des très gros projets, donc rarement pertinent pour le commun des mortels. Ça se mitige bien si le compilateur transforme le type de retour pour un tuple (typeDeRetour, Erreur) de façon transparente, et ajoute le sucre syntaxique à la volée. Genre ce que fait swift. Bon courage pour introduire ça en c++ par contre, le train a quitté la gare, comme on dit outre quebin.

        Le premier, ça devient plus compliqué. Déjà, les unchecked exceptions, c’est vraiment de la merde. Ça sort de nul part et paf, pastèque. Forcer à les déclarer aide. Forcer un try léger au call site aide aussi. Ça évite de faire un appel qui peut lancer, et le rater parce que ta signature inclue l’erreur lancée.

        Alors, certes, ta fonction peut aussi lancer, et celle qui l’appelle aussi, sur 50 niveaux, retour à la case départ, on a un goto déguisé. Pour être tout à fait honnête, si tes exceptions bubblent sur plus de 2 niveaux, t’as un très très très gros problème de leaky abstraction. Les couches ne sont pas isolées du tout si tu peux remonter plus de 2 niveaux. Et ça, c’est un bien plus gros problème que de savoir si les exceptions sont autorisees ou pas.

        ça nous laisse avec des cas réellement exceptionnels, qui se comptent sur les doigts d’une main. Genre « on a plus de ram » ou « un des core du cpu a disparu à la volée » et autres cas tarabiscotés du genre. J’ai pas de réponse pour ce cas là. Mais disons que c’est très très très loin d’être courant, et si ce genre de gestion représente plus de 0.00001% du code écrit, je serais surprit. Je serais déjà surprit si ne serait ce que 1% des projets gèrent ce genre de problèmes.

        Bref, au final, c’est un faux débat. Le concept même de l’exception (une erreur opaque que tu rebalance au niveau supérieur) est plutôt un anti pattern, et se gère beaucoup mieux avec un type result, ou un tuple syntaxique. Et vu le pot de pus que sont les exceptions en c++, je trouve pas ça délirant de les interdire.

        Linuxfr, le portail francais du logiciel libre et du neo nazisme.

        • [^] # Re: Pas de fumée sans feu

          Posté par  (site web personnel) . Évalué à 8.

          Vous enfoncez certainement tous les deux des portes ouvertes :) :

          • Le Google C++ style guide est vieux: pre-C++11. Il date d'une époque où la RAII n’était pas répandu et la majorité des code bases n'étaient pas exception safe. Dans ce cadre, oui l'exception ban était justifiée.

          • Pour information, Google est dit d'avoir une code-base C++ qui s'approche ou dépasse le milliard de ligne de C++, il est presque impossible de changer une règle sur les exceptions du jour au lendemain sur des code bases de cette taille sans grosses conséquences.

          • De l'aveu du Monsieur C++ Engineering chez Google, la policy serait certainement différente si c'était à refaire aujourd'hui.

          • Les exceptions (comme mechanisme, en dehors de C++) sont un excellent moyen de faire de l'error report de manière sure et concise sans un code trop verbeux.

          • En C++, l’implémentation actuelle du mechanisme d'exceptions souffrent de plusieurs problème ( RTTI, nécessite des allocations dynamiques, utilise l’héritage, UB quand appelée dans les destructeurs, nécessite un runtime, vulnerabilite DDOS, type leaking, etc… ) qui fait que même en 2022, certaines codebases ne peuvent pas se permettre de l'utiliser ( safety critical, kernel code, etc ).

    • [^] # Re: Pas de fumée sans feu

      Posté par  (site web personnel) . Évalué à 4.

      Je pense que c'est exactement ce pourquoi Google n'utilise pas les exceptions, et c'est leur strict le plus droit (du moment qu'ils n'embêtent pas les autres avec). Écrire du code "exception safe", ça veut effectivement dire de pleinement utiliser RAII et de ne jamais être en position d'avoir une fuite mémoire, de ressource, de transaction… Difficile à rétrofitter, donc. Mais quand le code est écrit avec les exceptions en tête, c'est toute une classe de problèmes qui disparaît. Et le code est généralement plus dense et lisible, la gestion des erreurs aux étages qui n'en tamponnent est complètement transparente.

      • [^] # Re: Pas de fumée sans feu

        Posté par  . Évalué à 2. Dernière modification le 08 août 2022 à 04:02.

        Ça existait les smart pointers a l'époque où a été rédigée le guideline ?

        Si ça existait il me semble que c'était très peu utilisé a l'époque, ce qui rendrait logique cette recommandation de ne pas utiliser d'exception.

        • [^] # Re: Pas de fumée sans feu

          Posté par  . Évalué à 3.

          Ca existait: std::auto_ptr. Il avait plusieurs problèmes, notamment, je cite:

          Because of these unusual copy semantics, auto_ptr may not be placed in standard containers

          Dans mon cas, je n'ai appris pour auto_ptr qu'en apprenant que C++0x (à l'époque) allait introduire un pointeur automatique (std::unique_ptr) qui résoudrait les problèmes d'auto_ptr. Les types qui se prétendaient profs que j'ai eu en BTS n'ont jamais parlé de cette fonctionnalité pourtant très importante.

          Je ne sais pas si mon cas peut être généralisé, mais au moins dans mon cas, la seule raison de non-utilisation d'auto_ptr dans mes codes ça a été: je savais pas que ça existait.

    • [^] # Re: Pas de fumée sans feu

      Posté par  (site web personnel, Mastodon) . Évalué à 8.

      Mon ressenti sur l'utilisation des exceptions, après avoir travaillé sur des projets en C et en C++ avec et sans exceptions, c'est plutôt l'inverse.

      Sans exceptions, on se retrouve à écrire au moins 3 lignes de code de gestion d'erreur par ligne de code "utile":

      Code utile:

      faire_un_truc();
      

      Avec gestion d'erreurs:

      result = faire_un_truc();
      if(result) {
          libérer_les_resources();
          return;
      }
      

      Si on a une fonction un peu plus longue qui alloue plusieurs resources, ça devient très vite le bazar de s'assurer que on a bien libéré toutes les ressources allouées.

      Alors que avec les exceptions et la RAII en C++, ça se fait tout seul: tous les objets alloués sur la pile sont automatiquement libérés lorsqu'on quitte la fonction.

      Bien sûr, pour que ça marche bien, il faut que toutes les resources soient liées à l'existence d'un objet sur la pile. Ce qui peut être compliqué si on en a pas l'habitude ou si le projet (ou les bibliothècues utilisées, ou l'OS ciblé) n'a pas été pensé pour.

      • [^] # Re: Pas de fumée sans feu

        Posté par  (site web personnel) . Évalué à 3.

        Sans exceptions, on se retrouve à écrire au moins 3 lignes de code de gestion d'erreur par ligne de code "utile":

        Heu… Sans les exceptions ne veut pas dire sans destructeur, donc ça donnerait plutôt :

        if (faire_un_truc()) {
            return;
        }

        Et comme avec les exceptions libérer_les_resources(); est à la charge du destructeur.
        Quand on compare, il faut comparer sans changer 2 paramètres pour en conclure sur 1 seul, il faut changer 1 paramètres et dans ton exemple tu as supprimé les exceptions et les destructeurs pour conclure sur les exceptions seules.

        ça devient très vite le bazar de s'assurer que on a bien libéré toutes les ressources allouées.

        Pas pire qu'avec les exceptions.

        Alors que avec les exceptions et la RAII en C++, ça se fait tout seul: tous les objets alloués sur la pile sont automatiquement libérés lorsqu'on quitte la fonction.

        Pareil avec un return, ou je n'ai pas compris l'idée.

        • [^] # Re: Pas de fumée sans feu

          Posté par  (site web personnel, Mastodon) . Évalué à 4. Dernière modification le 08 août 2022 à 11:10.

          En effet, je compare le C (avec ni exceptions ni destructeurs) avec le C++ (avec exceptions et destructeurs). Voyons la solution intermédiaire ou on aurait droit aux destructeurs mais pas aux exceptions.

          Ok, on passe de:

              faire_un_truc();

          à:

              if (faire_un_truc())
                  return;

          En admettant qu'on enlève les accolades pour gagner un maximum, on est quand même à 50% des lignes qui ne sont là que pour la gestion d'erreur. Et ça c'est dans un code simple ou il n'y a pas de boucles ou autres structures de contrôle compliquées.

          Mais, si on écrit le code comme ça, au-delà du bête calcul sur le nombre de lignes utiles, tout le code qui fait des trucs est "caché" dans des if() qui vérifient si les fonctions ont fonctionné. Et là on est dans un cas ou la fonction retourne juste un bool pour dire si elle a fonctionné. Mettons qu'on veut par exemple ouvrir un fichier.

          Version C++ avec exceptions et destructeurs:

              void OuvrirFichier() {
                  Fichier leFichier;
              }

          Si ça marche pas, la fonction lève une exception.

          Version C sans rien:

              void OuvrirFichier() {
                  Fichier* leFichier = open();
                  if (leFichier == NULL)
                      return;
                  close(leFichier);
              }

          Version C++ sans les exceptions:

              void OuvrirFichier() {
                  Fichier leFichier();
                  if (!leFichier.Ouvrir())
                      return;
              }

          Et ça c'est juste en regardant une fonction de très près. La classe ou structure Fichier va être plus compliquée: il faut une méthode Ouvrir qui retourne une erreur, il faut un destructeur qui vérifie si le fichier est vraiment ouvert avant de le fermer.

          Et là on a toujours une seule ressource dans cette fonction. Quand il y en a 2 ou plus, la complexité de la gestion d'erreur va augmenter. Il y a le risque d'oublier un cas d'erreur et de ne pas mettre un if là ou il faudrait, et de se retrouver avec un objet dans un état invalide (on peut avoir un objet Fichier qui ne correspond pas à un vrai fichier, soit parce qu'on n'a pas encore appelé la méthode Ouvrir, soit parce que la méthode a rencontré une erreur).

      • [^] # Re: Pas de fumée sans feu

        Posté par  . Évalué à 6.

        Avec la gestion des exceptions, c'est plutôt:

        try
        {
          faire_un_truc();
        }
        catch( auto& e )
        {
          throw;
        }
        

        Sans les exceptions:

        if( faire_un_truc() == ERROR )
        {
          return;
        }
        

        Ca, c'est pour le squelette individuel.
        Les exceptions sont extrêmement verbeuses dans des fonctions courtes (4 lignes effectives ici) quand on veut traiter une ou plusieurs erreurs, ce qui me semble être le but originel (si c'est juste pour logguer, franchement, on a vu mieux que les exceptions de C++, à commencer par celles de java qui affichent toute la stack automatiquement, et qui accessoirement a un compilateur qui râle, de mémoire, quand les exceptions ne sont pas toute gérées… c'est à dire une véritable intégration dans le langage).

        En théorie, sur une fonction longue, avec juste une partie des erreurs traitées (ou avec aucune traitées), ce qui serait l'intérêt, alors oui, le code est plus clean et plus court, mais bon perso j'évite au maximum les erreurs longues…
        Il y a aussi le problème que, dans un catch(), tu catches l'erreur de quelle ligne, au fait? En C++, l'info est perdue.

        Dans une gestion des erreurs traditionnelle avec RAII, je fait juste:

        #define xstr(s) str(s)
        #define str(s) #s
        #define HEADER_LOG __FILE__ " [" str( __LINE__ ) "]: " 
        ...
        
        class bla
        {
        public:
          bla( void ) = default;
          bla( bla const& ) = default;
          bla( bla&& ) = default;
          bla& operator=( bla const& ) = default;
          bla& operator=( bla&& ) = default;
          ~bla( void ) = default;
          //oupa, selon qu'on doit gérer des trucs nus...
        
          static bla create_bla_from_network( foobar )
          {
            bla ret;
            if ( do_something( foobar ) ) { return bla(); }
            return ret;
          }
          bool operator==( bla const& o ){...; return result; }
        };
        
        bool hello()
        {
          auto world = bla::create_bla_from_network( foobar );
          if( wold == bla() )
          {
            fputs( HEADER_LOG "bla bla\n", stderr );
            return true;
          }
        }
        

        Et je garde l'info de l'endroit ou le problème s'est produit, contrairement a ce que font les exceptions en C++.

  • # tradeoff

    Posté par  (site web personnel) . Évalué à 4.

    dans mes systèmes financiers, là où on va prendre une donnée, l'enrichir

    Toujours les mêmes qui s'enrichissent du système financier.

    Le style guide explique bien la décision dans le contexte de Google : https://google.github.io/styleguide/cppguide.html#Exceptions
    Il me semble évident que différents projets / organisations ayant différentes contraintes ne bénéficient pas forcément des mêmes compromis.

    J'ai pas vraiment d'avis dans ce cas ci mais « You are not Google » est quelque chose qui ressort assez souvent quand on parle de traitement de données à grande échelle. E.g. https://blog.bradfieldcs.com/you-are-not-google-84912cf44afb

    Avertissement : je travaille chez Google mais je fais très peu de C++.

    pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

  • # Nous ne sommes pas Google

    Posté par  . Évalué à 10.

    Nous ne sommes pas Apple.
    Nous ne sommes pas Facebook.
    Nous ne sommes pas Amazon.
    Nous ne sommes pas Microsoft

    Est ma première réponse quand on m'avance l'argument : « GAFAM l'utilise ».

    Ensuite je creuse :

    • quels sont les avantages/inconvénient ?
    • avons-nous les ressources en interne pour gérer (temps/expérience/argent)
    • n'est-ce pas un peu overkill tous ces microservices sur un EC2 ?… pour un formulaire de contact utilisé trois fois par mois et si ça tombe en panne une nuit c'est pas grave…
    • n'est-ce pas un peu compliqué ?
    • quelle dette technique ?

    Puis la fièvre retombe et le patient reprend ces esprits :

    • effectivement, c'est un peu too much
    • par contre quelques points sont intéressants et on pourrait refactoriser certaines parties de notre app.

    OK, prends la journée pour mettre en place un POC. Demain matin tu nous le présentes et on avise.

  • # Une autre vision des exceptions

    Posté par  (site web personnel) . Évalué à 7.

    Ma modeste contribution au débat…

    Soit le pseudo-code suivant :

    Lire octet
    TantQue octet != 0
      Si octet == 1
        Faire ActionA   
      Sinon Si octet == 2
        Lire octet
        Si octet == 0
          Faire ActionB
        Sinon
          Faire ActionC
        FinSi
      Sinon
        Faire ActionD
      FinSi
      Lire octet
    FinTantQue

    Transcription naïve en C, en utilisant getc (j'écarte volontairement l'utilisation de switchcase) :

    int main() {
      int c;
    
      c = getc();
    
      while ( c != 0 ) {
        if ( c == 1 )
          ActionA();
        else if ( c == 2 ) {
          if ( getc() == 0 )
            ActionB();
          else
            ActionC();
        } else
          ActionD();
    
        c = getc();
      }
    
      return EXIT_SUCCESS;
    }

    Avec ce code, s'il y a le moindre problème avec le flux lu par getc(…), on rentre dans une boucle infinie.

    Implémentons maintenant une gestion d'erreurs à la C.

    int main() {
      int c;
    
      c = getc();
    
      if ( c == EOF )
        return EXIT_FAILURE;
    
      while ( c != 0 ) {
        if ( c == 1 )
          ActionA();
        else if ( c == 2 ) {
          c = getc();
    
          if ( c == EOF )
            return EXIT_FAILURE;
    
          if ( c == 0 )
            ActionB();
          else
            ActionC();
        } else
          ActionD();
    
        c = getc();
    
        if ( c == EOF )
          return EXIT_FAILURE;
      }
    
      return EXIT_SUCCESS;
    }

    On voit que, par rapport à la transcription naïve du pseudo-code, on a a doublé le nombre de if à cause de la gestion d'erreurs. Sans compter que j'ai fait l'impasse sur la gestion d'erreurs des fonctions ActionX !

    Utilisons maintenant les exceptions :

    int get(void) {
      int c = getc();
    
      if ( c == EOF )
        throw ;
      else return c;
    }
    
    int main() {
      int c;
    
      try {
        c = get();
    
        while ( c != 0 ) {
          if ( c == 1 )
            ActionA();
          else if ( c == 2 ) {
            if ( get() == 0 )
              ActionB();
            else
              ActionC();
          } else
            ActionD();
    
          c = get();
        }
      } catch () {
        return EXIT_FAILURE;
      }
    
      return EXIT_SUCCESS;
    }

    On voit que le code entre le try et le catch correspond presque ligne à ligne avec le pseudo-code correspondant. Je précise que l'ensemble du code ci-dessus est quand même une énorme simplification de la manière dont j'utilise les exceptions.

    Pour résumer, j'utilise les exceptions afin que mon code soit très proche du pseudo-code que l'on pourrait écrire pour l'algorithme correspondant, ce qui, d’après moi, facilite grandement sa compréhension, sa maintenabilité et son évolutivité, en évitant qu'il ne soit pollué par le code dédié à la gestion des erreurs.

    Cette approche, je l'ai mise en œuvre alors que je programmais encore en C, lorsque je ne connaissais ni le C++, ni son mécanisme d'exceptions. Les throw, try, catch étaient simulés à l'aide de macros s'appuyant sur la bibliothèque setjmp. Ces macros garantissaient par ailleurs que toutes les ressources étaient correctement libérées lors de la survenue d'une «exception» (en fait, un appel à longjmp).

    Lorsque je suis passé au C++, j'ai réimplémenté ces macros en m'appuyant sur les exceptions, tout en conservant leur implémentation originale, les premiers compilateurs C++ n'implémentant pas tous les exceptions.

    Avec cette approche, je n'ai jamais rencontré de problèmes particuliers avec les exceptions. Pour autant, je n'irais pas jusqu'à affirmer qu'elle n'a pas son lot d'inconvénients qui la rendrait inapplicable dans certaines situations…

    Cyberdépendance, cyberharcèlement, pédocriminalité… : Zelbinium, pour que les smartphones soient la solution, pas le problème !

    • [^] # Re: Une autre vision des exceptions

      Posté par  (site web personnel) . Évalué à 3. Dernière modification le 08 août 2022 à 11:21.

      Dans ce cas là ça facilite les choses, mais dans une machine à états tu serais plus ou moins bloqué si tu ne sais pas à quel moment le getc n'a pas marché et donc revenir « en arrière » ne serait pas possible.

      Les exceptions peuvent souvent aider mais ils n'apportent pas toujours la réponse à toutes les solutions.

      git is great because linus did it, mercurial is better because he didn't

    • [^] # Re: Une autre vision des exceptions

      Posté par  . Évalué à 1. Dernière modification le 13 septembre 2022 à 12:15.

      Après, vu l'état du code, c'est déjà un cauchemar à lire, sans la gestion des erreurs.

      Pourquoi? Parce que ce genre de code spaghetti n'a pas été pensé comme il le faut.
      D'ailleurs, exception ou pas, tu as une boucle infinie dans ton code (si getc() > 2 ou al).

      Si c'est un parseur que tu veux, tu ne fais pas "en série", sauf cas très simple, mais "en parallèle" avec une gestion d'état. Et là, magie, le code devient beaucoup plus simple à écrire et parser, exceptions ou pas.

      State get(State s) 
      {
         int c = getc();
         if (c == EOF)        return EOF;
      
         // Logique du parseur ici
         switch(s) 
         {
         case Start: /* Transition de start à XXX */ 
            if (c == 1)       return ActionA; 
            else if (c == 2)  return NeedMoreInput; 
            return ActionD;
         case NeedMoreInput: /* Transition de start à ActionB ou C */
            if (c==0)         return ActionB; 
            return ActionC;
         default: return EOF;
         }
      }
      
      void parse() {
        enum State s = Start;
        while (true) {
          s = get(s);
          // Action du parseur ici
          switch(s) {
             case ActionA : actionA(); break;
             case ActionB : actionB(); break;
             case ActionC : actionC(); break;
             case ActionD : actionD(); break;
             case EOF : return;
          }
        } 
      }

      Utiliser des exceptions pour faire du retour de fonction, c'est créer 2 chemins (ou plus), là où il n'y en avait qu'un avant. Tu fais ça sur 10 niveaux, tu as maintenant 1024 chemins (ou plus) à tester et valider (donc 10x plus de code de test à écrire). Je te souhaite bien du courage.

      Après, si une exception ne fuite pas vers le haut (difficile en général à réaliser), alors oui, les exceptions peuvent être utiles pour simplifier le code, mais il n'y a, dans ce cas, quasiment jamais d'avantage par rapport à des retours normaux bien gérés.
      Par contre, avec l'aide de macro simples (remplaçant le try), tu peux capturer un callstack et là oui, c'est super bien pour retrouver le problème et simplifie fortement le code de log (uniquement à destination d'un développeur, car pour le pékin moyen, le log c'est mieux).

      • [^] # Re: Une autre vision des exceptions

        Posté par  (site web personnel, Mastodon) . Évalué à 2.

        D'ailleurs, exception ou pas, tu as une boucle infinie dans ton code (si getc() > 2 ou al).

        J'ai cru lire que tous les cas autres que 1,2,0 sont traités par ActionD; probablemnte les effets du manque de sommeil.

        “It is seldom that liberty of any kind is lost all at once.” ― David Hume

  • # À propos des exceptions

    Posté par  (site web personnel) . Évalué à 2.

    C'est vrai que la questions de l'utilisation des exceptions reste toujours à débat chez les développeurs C++. Je suis ni pour ni contre mais je dois tout de même admettre que dans les langages modernes on arrive quand même à s'en passer (Rust, Go). Je les utilisais pas mal quand je faisais du C++ mais sur certains points c'est pas toujours pratique notamment dans un bloc de code comme ceci :

    int val;
    
    try {
        val = std::stoi("foo");
    } catch (...) {
    }
    
    continue_working_with_val(val);

    Actuellement je fais que du C et j'arrive à me débrouiller sans les exceptions (on peut faire du sjlj mais c'est quand même fastidieux). Alors, allons continuer vers des langages sans exceptions ?

    git is great because linus did it, mercurial is better because he didn't

  • # Juste mon point de vue

    Posté par  (site web personnel) . Évalué à 8.

    Moi je trouve très bien que des boîtes comme Google partagent leurs règles de codage, surtout quand les choix sont justifiés. Sur le plan éthique j'aurais des choses à redire de Google ou Facebook mais sur le plan technique ils ont plus à nous apprendre que l'inverse.

    Je me suis d'ailleurs parfois appuyé sur eux (et sur Linux, Mozilla et d'autres) pour justifier certains choix pour lesquels tout le monde a un avis. Puisqu'on ne peut tomber d'accord en interne on va faire comme les autres, et celui qui n'est pas d'accord se charge d'écrire à Mozilla/Google/autre pour leur expliquer qu'ils se débrouillent mal. Ça n'a pas toujours été la solution qui m'arrangeait mais ça permet de débloquer les discussions.

    Je sais bien que c'est bien confortable pour eux puisque du coup c'est le reste du monde qui s'adapte à leurs pratiques. Ça leur fait toujours ça de moins à expliquer aux nouvelles recrues. Mais ce n'est pas une bonne raison pour ne pas écouter ce qu'ils ont à dire.

    Pour moi les questions à se poser concernant n'importe quelle pratique sont :

    • pourquoi les pros font comme ils font ?
    • qu'est-ce que ça donnerait à mon échelle ?
    • qu'est-ce que ça va me coûter si je change d'échelle ?

    Concernant les exceptions en particulier, j'ai vu tellement de cochonneries où elles traversaient justement plusieurs couches et qu'on ne pouvait de toute façon rien en faire, ou, pire, elles étaient lancées et rattrapées dans la même fonction, que je préfère éviter autant que possible. Ce n'est pas une question de perfs, c'est une question d'éviter le bazar et d'éviter le cas du fainéant qui va passer l'erreur à son voisin plutôt que de la gérer lui-même. En fait je me demande si les exceptions ne seraient pas le pire mécanisme de gestion d'erreur qui soit.

    Je préfère largement quelqu'un qui ne veut pas mettre d'exception juste parce que Google a dit de ne pas le faire plutôt que quelqu'un qui en met systématiquement parce que son prof, qui n'a jamais écrit que du code confidentiel, lui a présenté ça comme une bonne manière de faire.

    Du coup pour un max dans une collection, puisque la solution du max_element est exclue, je mets en précondition que la collection ne soit pas vide. C'est l'appelant qui doit gérer sous peine de comportement indéfini, et comme je suis cool je mets quand même un assert en debug :)

    • [^] # Re: Juste mon point de vue

      Posté par  . Évalué à 3. Dernière modification le 08 août 2022 à 00:40.

      Du coup pour un max dans une collection, puisque la solution du max_element est exclue, je mets en précondition que la collection ne soit pas vide. C'est l'appelant qui doit gérer sous peine de comportement indéfini, et comme je suis cool je mets quand même un assert en debug :)

      Je ne sais pas si c'est la meilleure façon de faire ou pas en C++, mais avoir une UB pour un fonctionnement aussi basique je trouve ça fou.

      On ne sait pas faire une recherche dans une collection de manière totalement safe ?

      C'est bien sûr une question de subjectivité, mais je préfère largement que ce soit fiable par défaut plutôt que piégeux quitte à ce que celui que ça gène d'avoir cette sécurité se pose des questions.

      Après réflexion j'imagine que de toute manière la recherche par itérateur produit de toute manière une UB si les 2 itérateurs ne sont pas mis dans le bon ordre ou s'ils ne concernent pas la même collection. Je ne suis clairement plus dans le mood pour le C++ :)

      https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

      • [^] # Re: Juste mon point de vue

        Posté par  (site web personnel) . Évalué à 4.

        On ne sait pas faire une recherche dans une collection de manière totalement safe ?

        Si, mais si l'appelant sait qu'il n'y aura jamais une liste vide c'est dommage d'avoir à le retester dans une fonction pour rien. Surtout dans un programme à forte performance comme un jeu vidéo.

        git is great because linus did it, mercurial is better because he didn't

        • [^] # Re: Juste mon point de vue

          Posté par  . Évalué à 3.

          Surtout dans un programme à forte performance comme un jeu vidéo.

          Il a bon dos le jeux vidéos. Les plus gros jeux vidéos font un tas de choses bien plus lourdes et contraignantes pour l'expérience du joueur comme avec les anticheat. Prendre en compte qu'une collection est vide ou non ne représente probablement pas un si gros problème et si on a besoin de vérifier 24 fois par seconde qu'une même collection contiens ou non quelque chose il faut peu être revoir quelque chose et se demander si utiliser un algo linéaire est une bonne idée. Note aussi que les jeux vidéos sont bien chargés en bug, il serait peut-être intéressant d'arrêter de sacrifier la fiabilité sur l'autel des performances.

          Mais il y a évidemment des cas de jeux qui regardent tout un peu comme sur la scène demo. Mais je ne crois pas qu'une api qui se veut généraliste doivent être par défaut prévu pour un cas d'usage relativement confidentiel quand celui là a une incidence sur tous les usages.

          En plus pour moi la gestion du fait qu'il s'agisse d'une liste vide ou pas devrait être à la charge de cette méthode. Si on imagine une méthode indiquant l'index d'un élément dans une collection et qui renvoie -1 s'il ne trouve pas, on trouverait logique que lui donner une liste vide devrait renvoyer -1. Ce n'est que parce que l'API via itérateur pose des questions qu'on a envi de faire gérer cette situation par l'appelant

          https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

          • [^] # Re: Juste mon point de vue

            Posté par  (site web personnel, Mastodon) . Évalué à 5. Dernière modification le 08 août 2022 à 12:48.

            Suite à cette discussion, je regardais comment le cas est géré en Java (je ne connais pas C++), et c’est intéressant parce que les deux méthodes génériques pour calculer le maximum d’un ensemble ont deux fonctionnements différents de par leur API, ce qui rejoint bien le propos de barmic ci-dessus.

            L’API Collections (Java 1.2 et plus) a une méthode qui permet de calculer le maximum d’une collection d’éléments comparables. Si la collection fournie est vide, ça lance une NoSuchElementException (qui est une exception non-checkée, donc qu’il n’est pas obligatoire de gérer dans les couches supérieures). De plus, le code ne compilera pas si les éléments ne sont pas comparables, et renverra une ClassCastException (non-checkée) si on essaie de comparer des choux et des carottes.

            Java depuis Java 8 contient aussi une API de streams, qui déteste les exceptions – le dédoublement du flux d’exécution est à peu près incompatible avec le concept même des streams. Donc il a fallu trouver une solution alternative.

            Dans le cas des streams, le calcul du maximum renvoie un Optional, à charge de l’appelant de vérifier si cette valeur optionnelle est vide ou non. On peut facilement récupérer le comportement à exceptions en faisant : myStream.max().orElseThrow(NoSuchElementException::new) (là encore au choix de l’appelant).

            Concernant Java, il y a un paradoxe assez étonnant qui fait que d’une part à peu près tout le monde considère que les checked exceptions sont une erreur de conception fondamentale du langage, mais en même temps que tout le monde les utilise. Même si depuis Java 8 et l’utilisation de plus en plus massive des streams, ce deuxième point est de moins en moins vrai.

            La connaissance libre : https://zestedesavoir.com

          • [^] # Re: Juste mon point de vue

            Posté par  . Évalué à 5.

            si on a besoin de vérifier 24 fois par seconde qu'une même collection contiens ou non quelque chose il faut peu être revoir quelque chose

            En même temps, si 24 tests d'égalité de deux pointeurs par seconde suffisent pour dégrader tes perfs, je pense qu'un investissement dans un processeur fabriqué après 1970 s'impose :-)

            L'optimisation prématurée est aussi un problème.

      • [^] # Re: Juste mon point de vue

        Posté par  (site web personnel) . Évalué à 0. Dernière modification le 08 août 2022 à 11:32.

        Je ne sais pas si c'est la meilleure façon de faire ou pas en C++, mais avoir une UB pour un fonctionnement aussi basique je trouve ça fou.

        Avoir une UB pour un fonctionnement impossible en réalité (que faire d'un max de conteneur vide? Le problème est ailleurs si on demande ça) me semble pas si fou que ça, ça pourrait faire des optimisations même (le choix de max_element() semble éviter l'impact de perf donc ça va encore, mais sinon il faut toujours tester si le conteneur est vide alors qu'il ne l'est jamais en vrai). Dans tous les cas ton code appelant doit gérer ce cas, une lib ne peut pas savoir ce que tu voudrais faire de ça si ça arrive…

        • [^] # Re: Juste mon point de vue

          Posté par  . Évalué à 2.

          Dans tous les cas ton code appelant doit gérer ce cas, une lib ne peut pas savoir ce que tu voudrais faire de ça si ça arrive…

          C'est tout le sujet de tous les commentaires du journal comment une bibliothèque fait pour dire à son utilisateur "là il y a un cas que je ne sais pas traiter".

          Traiter cela avant l'appel peut être complexe car tu n'a pas de moyen de t'assurer de toutes les préconditions. Et il y a des cas où c'est simplement impossible de faire les vérifications avant.

          https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

          • [^] # Re: Juste mon point de vue

            Posté par  . Évalué à 4.

            comment une bibliothèque fait pour dire à son utilisateur "là il y a un cas que je ne sais pas traiter".

            Avec de la doc.

            Je préfère une lib qui est documentée correctement et qui ne vérifie (explicitement) pas des pré-conditions, ça me permets d'avoir un code comme ceci:

            void foo( std::vector<int> const& bar )
            {
            
              if( bar.empty() ) { return; }
              std::vector<int>::iterator it1 = std::find_if( std::begin( bar ), std::end( bar ), []( int v ){ return v > 10; } )
              if( std::end( bar ) == it1 ){ return; }
              std::vector<int>::iterator it2 = std::find_if( std::begin( bar ), std::end( bar ), []( int v ){ return v < 10; } )
              if( std::end( bar ) == it2 ){ return; }
            }
            

            Sans que std::find_if n'ait besoin de vérifier a chaque fois que le vector est bien peuplé.

            Le fait que le vector ne soit pas peuplé ne relève clairement pas de la responsabilité de std::find_if, ni de bien d'autres fonctions, et souvent on fait plusieurs opérations sur les collections, et il est plus pertinent en terme de perfs de ne faire le boulot qu'une seule fois plutôt qu'a chaque appel, d'autant que ça peut vite partir en cascade.

            Alors, oui, ça implique que le dev sache lire une doc… mais savoir lire une doc, ça me paraît la base du métier: on lit de la doc en permanence, a commencer par les documents (plus ou moins bien faits) qui décrivent le besoin auquel on doit répondre.
            Et le C++ n'a jamais fait de mystère qu'un de ses objectifs principaux, c'est la performance. Faire un truc rapide sans avoir de compréhension de la lib standard, peut importe le langage, ça me semble très… disons difficile.

            • [^] # Re: Juste mon point de vue

              Posté par  . Évalué à 0.

              Avec de la doc.

              C'est un choix qui est discutable (si tout se passait aussi bien que tu le dis ce ne serait pas un sujet) et pas généralisable.

              Sans que std::find_if n'ait besoin de vérifier a chaque fois que le vector est bien peuplé.

              Je veux bien voir un bench réaliste qui montrerais l'impact réel de ce genre de trucs. L'assomption "on s'en fout de la sécurité, on veut de la perf à tout prix" me semble des fois être plus de l'ordre du dogme que d'un choix rationnel. Qu'une méthode en O(n) s'inquiète pour des raisons de performance d'un traitement en temps constant qui consiste à tester la valeur d'un entier, ça me paraît pas cohérent.

              d'autant que ça peut vite partir en cascade.

              Non parce que tu la gère cette erreur. Tu la gère après et pas avant.

              Et le C++ n'a jamais fait de mystère qu'un de ses objectifs principaux, c'est la performance. Faire un truc rapide sans avoir de compréhension de la lib standard, peut importe le langage, ça me semble très… disons difficile.

              Donc pour toi tous les pièges qu'on peut trouver dans une bibliothèque standard ou mieux dans le langage lui même ne sont jamais un problème : les utilisateurs doivent à minima avoir une compréhension du langage pour s'en servir.

              https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

              • [^] # Re: Juste mon point de vue

                Posté par  (site web personnel) . Évalué à 2. Dernière modification le 11 août 2022 à 13:23.

                les utilisateurs doivent à minima avoir une compréhension du langage pour s'en servir.

                Rappelons qu'on parle d'un cas où la demande à l'algo n'a aucun sens.
                Perso ça ne me choque pas qu'une lib fasse n'importe quoi quand on lui demande n'importe quoi.
                Spolier: la lib standard C++ fait déjà n'importe quoi dans plein de cas où on lui demande n'importe quoi, donc s'en offusquer ici et pas de manière générale est un peu bizarre.

                Si on avait déjà une lib qui n'a pas d'autres UB… Et si on veut une bonne exception sans se soucier de perfs, bon ben utilisons donc Python (oui, troll :-p).

                • [^] # Re: Juste mon point de vue

                  Posté par  . Évalué à 3.

                  Rappelons qu'on parle d'un cas où la demande à l'algo n'a aucun sens.

                  Bien sûr que ça a du sens. Ce n'est pas parce que c'est vide que ça n'a pas de sens, c'est un cas particulier triviale mais ça a tout à fait un sens. Ça a du sens de faire un select sur une table vide, un find dans un dossier vide, un grep sur un fichier vide,… Tu ne cherche jamais quelque chose dans un tiroir ou une boîte vide ?

                  C'est la façon dont on a envi de faire l'api qui le rend embêtant.

                  https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

              • [^] # Re: Juste mon point de vue

                Posté par  (site web personnel) . Évalué à 3.

                Je veux bien voir un bench réaliste qui montrerais l'impact réel de ce genre de trucs.

                Que je sache il n'y a pas de tel benchmark, simplement parce qu'on parle d'un coût faible qui ne ressort que lorsqu'on regarde une appli dans son ensemble. Le profiler ne pointera jamais tous les ifs éparpillés dans l'app, mais ça ne veut pas dire que leur coût est négligeable.

                Pour moi il y a deux mondes : les logiciels pleins de « on ne sait jamais », « ça sera future proof », « ça coûte moins cher d'acheter une machine plus puissante » ; qui sont la raison pour laquelle on trouve que les ordis d'aujourd'hui prennent plus de temps à faire des choses simples que les ordis d'hier, et de l'autre côté un monde où on ne fait pas ce qui n'est pas essentiel, où on ne s'engage que pour certains cas d'utilisation, et qui tire fortement profit de la puissance des machines.

                • [^] # Re: Juste mon point de vue

                  Posté par  . Évalué à 2.

                  Que je sache il n'y a pas de tel benchmark, simplement parce qu'on parle d'un coût faible qui ne ressort que lorsqu'on regarde une appli dans son ensemble. Le profiler ne pointera jamais tous les ifs éparpillés dans l'app, mais ça ne veut pas dire que leur coût est négligeable.

                  La majorité des bench ne passent pas par des profilers, mais donnent un temps d’exécution global. Ça doit pouvoir se tester.

                  Pour moi il y a deux mondes

                  Pour moi aussi : il y a ceux qui continue à créer des CVE liées à la mémoire par exemple et ceux qui tentent de supprimer les bugs dès les API (oui on peut être caricatural).

                  Quoi qu'il arrive je pense qu'on tourne en rond. Je ne suis clairement pas câblé retourner vers du C++ et vous me le confirmer. À titre tout à fait personnel ça me donne envi de privilégier rust partout où c'est possible, je sais que ça irrite les développeurs C++, mais si rust c'est des perfs sans pour autant sacrifier à la fiabilité, ça conviendra plus à mes aspirations.

                  https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                  • [^] # Re: Juste mon point de vue

                    Posté par  (site web personnel) . Évalué à 3.

                    À titre tout à fait personnel ça me donne envi de privilégier rust partout où c'est possible, je sais que ça irrite les développeurs C++, mais si rust c'est des perfs sans pour autant sacrifier à la fiabilité, ça conviendra plus à mes aspirations.

                    Pas de souci avec le Rust, c'est un super langage qui résout des problèmes du c++ et ne se traîne pas des décennies d'historique :)

                    • [^] # Re: Juste mon point de vue

                      Posté par  . Évalué à 2.

                      Mon seul souci avec le Rust, c'est ma propre résistance au changement, j'ai la flemme de l'apprendre.
                      Bon, il y aurait peut-être aussi le "tout est lié en statique", mais je ne sais pas si cette restriction est réelle ou s'il n'y a pas un moyen de garder des libs dynamiques (de la même manière que j'ose espérer qu'on n'est pas obligé de passer par un repo npm-style, mais ça j'en suis presque sûr que c'est possible), qui n'est pas vraiment lié au langage lui-même si à sa lib de toute façon.

                      Sur le papier, il a tout pour me plaire, pourtant, sauf peut-être la très très grosse compat avec le C (en C++, il y a juste rien à faire pour utiliser une lib C. J'imagine que ça ne dois pas être le cas de rust, mais en vrai je ne sais pas). Pas sûr que ce point suffirait a m'empêcher d'utiliser rust, par contre.

                      Ceci dit, question: j'ai cru lire que rust n'utilisais pas d'exceptions, donc, sur la problématique de l'erreur d'un "find max value in range", comment ça marche? Retour d'un équivalent de std::option j'imagine? Parce que bon, utiliser un paramètre de sortie, type error_t find_max_in( start, end, result ) c'est assez moche (force a déclarer les variables avant, impossible de chaîner les appels, etc), donc je ne vois pas vraiment d'autre solution.

                      • [^] # Re: Juste mon point de vue

                        Posté par  . Évalué à 4.

                        Apparemment Rust va utiliser l'approche "optional". Si le conteneur est vide, l'élément None est retourné.

                        • [^] # Re: Juste mon point de vue

                          Posté par  . Évalué à 3.

                          Ca fait sens. Il y a fort a parier que la raison que C++ ne fasse pas ça, c'est… tout simplement son âge. Et le refus de casser des APIs importantes (parce que largement utilisé).

                          Du coup, il faut quand même vérifier que le retour est None, ou un truc de ce type. D'un point de vue "apparence de code", ça doit donc être relativement similaire, du coup.

                          Ceci dit, je m'aperçois en écrivant ça qu'il y a un problème important avec le retour d'itérateur qui n'a pas été abordé dans la discussion, plus important que l'UB de déréférencer un élément hors-limite: il est possible d'accéder à un élément valide, ce qui génère un bug, si on donne un sous-ensemble à manger à, disons, std::find.
                          Je trouve que c'est plus gênant que l'UB, parce que l'UB on peut contourner le problème avec des canary ou UBsan, et j'imagine que c'est une des choses que Rust fait par défaut (on ne peux pas tout vérifier à la compilation, après tout).

                          J'espère que le C++ finira par avoir des versions des algo qui retournent un optional. Je trouve ça bien mieux que les exceptions, de manière générale, plus lisible, et le compilateur peut vérifier que les cas sont traités. Si ils pouvaient en profiter pour faire la même avec les conteneurs, ça serait bien aussi.

                          • [^] # Re: Juste mon point de vue

                            Posté par  . Évalué à 2.

                            Ca fait sens. Il y a fort a parier que la raison que C++ ne fasse pas ça, c'est… tout simplement son âge. Et le refus de casser des APIs importantes (parce que largement utilisé).

                            C'est mon point de vu : c++ adore les api à base d'itérateur sauf que ça produit des cas d'erreur et des cas complexes. Je peux te donner un itérateur de début et de fin qui ne concernent pas le même conteneur par exemple. Mais l'argument du "derrière un itérateur on peut avoir ce qu'on veut" est limité parce que tu ne recherche pas dans un tas comme tu recherche dans une liste et ton api ne te permet pas d'utiliser le polymorphisme pour ça.

                            D'autres ici avancent que la gestion des cas triviaux ajoute un overhead inadmissible dans du code c++.

                            Du coup, il faut quand même vérifier que le retour est None, ou un truc de ce type. D'un point de vue "apparence de code", ça doit donc être relativement similaire, du coup.

                            Ça dépend de comment est implémenté l'optional. Si l'optional en question intègre dans son typage la présence ou non d'une valeur (si tu as un type somme ou si optional est une interface implémentée par 2 classes present/absent et seul present permet d'accéder à la valeur) alors ton système de type peux garantir compile time que tu gère les cas d'erreur.

                            https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                            • [^] # Re: Juste mon point de vue

                              Posté par  . Évalué à 3.

                              D'autres ici avancent que la gestion des cas triviaux ajoute un overhead inadmissible dans du code c++.

                              Je pense toujours qu'il est dommage de vérifier le fait qu'un conteneur soit vide a chaque interaction. Par contre, je suis d'accord que l'interface n'est pas optimale et pourrais être améliorée.

                              Si l'optional en question intègre dans son typage la présence ou non d'une valeur … alors ton système de type peux garantir compile time que tu gère les cas d'erreur

                              Pas vraiment. Il faut déjà que le compilateur soit informé que la non vérification de la valeur de retour d'une fonction est une erreur (ce que C++17 introduit via [[nodiscard]] mais a ma connaissance ce n'est pas encore utilisé dans l'API standard, ce qui est dommage).

                              Cette fonctionnalité est ce qui permets à mon avis d'avoir un minimum de garanties à la compilation sur ce genre de bugs (bien bêtes, en plus, et pénibles a trouver), l'optional est "juste" un détail d'implémentation qui permets d'avoir une interface cohérente, mais ce n'est pas le seul cas, typiquement, si je fais juste foobar.empty(); ou foobar.size();, il est évident qu'il y a des bugs, et pourtant il n'y a aucune raison de renvoyer un optional.

                              • [^] # Re: Juste mon point de vue

                                Posté par  . Évalué à 2.

                                Pas vraiment.

                                Je suis allé un peu vite et je ne doute pas que ça a un overhead (il faut utiliser le RTTI), mais si tu fais quelque chose du genre :

                                template <class T>
                                struct Optional {
                                };
                                
                                template <class T>
                                struct Present<T> : public Optional<T> {
                                    virtual T get();
                                };
                                
                                // …
                                
                                var a = foo();
                                if (Present* b = dynamic_cast<Present*>(a)) {
                                    b->get().bar();
                                }

                                Le fais de ne jamais avoir fais d'opération dangereuse dans ton système de type te garanti que tu as fais la vérification, mais oui c'est pas très idiomatique je pense.

                                Cette fonctionnalité est ce qui permets à mon avis d'avoir un minimum de garanties à la compilation sur ce genre de bugs (bien bêtes, en plus, et pénibles a trouver), l'optional est "juste" un détail d'implémentation qui permets d'avoir une interface cohérente, mais ce n'est pas le seul cas, typiquement, si je fais juste foobar.empty(); ou foobar.size();, il est évident qu'il y a des bugs, et pourtant il n'y a aucune raison de renvoyer un optional.

                                Je comprends tout à fait. J'avais raté les attributes (arrivés en C++11 à ce que je vois).

                                https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

              • [^] # Re: Juste mon point de vue

                Posté par  . Évalué à 3.

                C'est un choix qui est discutable (si tout se passait aussi bien que tu le dis ce ne serait pas un sujet) et pas généralisable.

                A un moment, je trouve ça quand même très gênant d'avoir des gens incapables de lire une doc aussi simple que celle de, par exemple, std::find_if

                d'autant que ça peut vite partir en cascade.

                Non parce que tu la gère cette erreur. Tu la gère après et pas avant.

                Je parlais du fait d'imbriquer des appels qui font faire encore et encore le même contrôle, pour rien, parce qu'il a déjà été fait avant.

                Au pire, si tu veux de la sécurité au lieu des perfs, tu peux très bien compiler avec UBsan/ASan, le programme plantera salement, tout comme si une exception avait été lancée sans être attrapée. Et en bonus: tu auras l'information de la ou est le problème, chose que l'on n'a pas avec les implementations d'exceptions en C++ (à ma connaissance).

                Donc pour toi tous les pièges qu'on peut trouver dans une bibliothèque standard ou mieux dans le langage lui même ne sont jamais un problème : les utilisateurs doivent à minima avoir une compréhension du langage pour s'en servir.

                Je n'ai pas dit ça.
                Je trouve juste que le paradigme de renvoyer out-of-bound en cas de non trouvé ou de conteneur vide, c'est pas déconnant du tout. Ca me semble personnellement plus propre que balancer des exceptions pour un oui ou pour un non.

            • [^] # Re: Juste mon point de vue

              Posté par  (site web personnel) . Évalué à 3.

              Faire un truc rapide sans avoir de compréhension de la lib standard, peut importe le langage, ça me semble très… disons difficile.

              Soit-dit en passant, le C++ est probablement le langage (avec le C, parmi les plus populaires) avec lequel on peut le plus facilement faire des trucs rapides sans rien connaître de la bibliothèque standard. Le C++ donne en effet directement accès à la bibliothèque C standard, aux bibliothèques systèmes, voire à l'assembleur. De quoi court-circuiter la bibliothèque C++ standard sans nécessairement perdre en performances. Il faut être motivé et avoir les compétences nécessaires, mais c'est plus facile qu'avec la plupart des autres langages, car on n'a pas besoin de changer d'environnement de développement pour ce faire …

              Cyberdépendance, cyberharcèlement, pédocriminalité… : Zelbinium, pour que les smartphones soient la solution, pas le problème !

              • [^] # Re: Juste mon point de vue

                Posté par  . Évalué à 2.

                C'est un peu ce que je fait, en pratique, et c'est vraiment l'intégration avec le C qui est à mon avis sa très grande force.
                Et de la même manière, rien ne force à utiliser la SL (et particulièrement la STL, je ne trouve pas que std::string, par exemple, soit une merveille d'ingénierie, je la remplace personnellement par un vector, ça fait le même boulot, en mieux, parce que plus générique, et de toute façon std::string ne comprend rien aux chaînes de caractères), même si je suis assez fan de #include <algorithm>, qui fournit des algo pratiques qui peuvent facilement marcher avec des conteneurs perso voire même de simples pointeurs (par exemple parce qu'on utilise une lib C pour une tâche précise).

    • [^] # Re: Juste mon point de vue

      Posté par  . Évalué à 2.

      Du coup pour un max dans une collection, puisque la solution du max_element est exclue, je mets en précondition que la collection ne soit pas vide. C'est l'appelant qui doit gérer sous peine de comportement indéfini, et comme je suis cool je mets quand même un assert en debug :)

      Du coup en release ta fonction est UB avec une collection vide ? :|

      Amha les assert ca ne devrait être utilisé que pour vérifier les conditions/invariants internes d'un soft/code en particulier.

      • [^] # Re: Juste mon point de vue

        Posté par  (site web personnel) . Évalué à 4.

        Du coup en release ta fonction est UB avec une collection vide ? :|

        C'est l'idée oui :)

        L'UB n'est pas qu'une mauvaise chose, ça veut juste dire que la fonction à un périmètre limité. Il y a plein de situations en UB et on ne met pas des verifications pour tout : le débordement d'un signed int, la comparaison d'itérateurs provenant d'instances de conteneurs différents…

        L'assert est très bien pour documenter ces cas et pour servir de garde fou pendant le développement.

        • [^] # Re: Juste mon point de vue

          Posté par  . Évalué à 3.

          J'ai une idée qui va mettre tout le monde d'accord j'en suis sûr.

          La fonction devrait renvoyer une collection contenant une copie de tous les éléments les plus grands ce qui lui permet de renvoyer zéro, un ou plusieurs éléments.

          Ben quoi ? 🙄
          (je suis sorti)

          https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

          • [^] # Re: Juste mon point de vue

            Posté par  (site web personnel, Mastodon) . Évalué à 2.

            En vrai ça pourrait être utile si l'ordre entre les éléments n'est pas un ordre total. Dans ce cas il peut en effet y avoir plusieurs maximums (éléments qui sont sont supérieurs à tous les autres, soit n'ont pas de comparaison définie avec les autres).

    • [^] # Re: Juste mon point de vue

      Posté par  . Évalué à 6.

      Je préfère largement quelqu'un qui ne veut pas mettre d'exception juste parce que Google a dit de ne pas le faire plutôt que quelqu'un qui en met systématiquement parce que son prof, qui n'a jamais écrit que du code confidentiel, lui a présenté ça comme une bonne manière de faire.

      Pour ma part je ne préfère aucun des deux ; dans les 2 cas c'est quelqu'un qui se remet à une instance supérieure pour faire ses choix. Je souhaite que les gens qui travaillent avec moi soient capables d'expliquer eux-mêmes leur choix.

  • # from rust

    Posté par  . Évalué à 3.

    Le coup du Result c'est plutôt une sorte de backport des langages modernes (Rust par exemple) en C++, et c'est surtout très malin (à défaut d'être efficace). Franchement si c'est pour économiser un test en C++, même dans une boucle, c'est vraiment chercher à sacrifier la lisibilité et la maintenance sur l'hotel de la performance. Parfois c'est pas ce que l'on veut.

    Les exceptions doivent rester le cas "exceptionnel", un cas d'erreur, un grave manquement dans le flot d'exécution. Une liste vide n'est pas un cas d'erreur. C'est un cas normal. Donc non, pas d'exception dans ce cas là. #testisgood.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.