C++17 fixe l’ordre d’évaluation des expressions

Posté par  . Édité par Oliver, Davy Defaud, Benoît Sibaud et palm123. Modéré par claudex. Licence CC By‑SA.
Étiquettes :
56
1
déc.
2016
C et C++

Le C++ est un langage bien présent et depuis longtemps dans les logiciels libres (environnements de bureau, outils bureautiques, navigateurs Web…). L’an 2017 approche à grands pas avec la promesse d’un tout nouveau C++17.

Pour finir l’année, voici le calendrier de l’Avent du C++ avec des dépêches pédagogiques sur ce qui nous attend en 2017. Après deux dépêches de mise‐en‐bouche, nous entrons enfin dans le vif du sujet avec deux spécifications techniques concernant l’ordre d’évaluation des expressions. Allez, c’est parti !   ᕕ(ᐛ)ᕗ

Bjarne propose de changer le C++ pour corriger son livre qu'il tient dans ses mains

Sommaire

Série de dépêches C++

Cette dépêche fait partie de toute une série disponible également sur le dépôt Git du Groupe des Utilisateurs C++ Francophone. Alors que cet article restera figé sur le site LinuxFr.org, il continuera d’évoluer sur le dépôt Git. Merci de nous aider à maintenir ce document à jour avec vos questions/suggestions/corrections. L’idée est de partager ce contenu libre et de créer/enrichir des articles Wikipédia quand la licence sera CC BY-SA 4.0.  

Publication Dépêche
20 août 2016 Les coulisses du standard C++
2 oct. 2016 Genèse du C++17
1ᵉʳ déc. 2016 C++17 fixe l’ordre d’évaluation des expressions
à venir… … d’autres dépêches …
en 2017 Faut‐il continuer à apprendre le C++ ?

Initialement, nous allions publier une grosse dépêche super trop longue. Puis, fin novembre, nous prenions conscience que les lecteurs apprécieraient plutôt une petite dépêche par jour, d’où l’idée de faire le calendrier de l’Avent du C++.   (ღˇ◡ˇ)~♥

Spécifications Techniques

Deux TS ont été amendés par le comité de normalisation du C++ afin de fixer l’ordre d’évaluation des expressions :

  • P0145 définit les changements nécessaires au standard C++ ;
  • P0400 reformule une phrase de cette précédente TS P0145.

Anecdote

Le livre mythique The C++ Programming Language de l’inventeur du C++, Bjarne Stroustrup contient une erreur subtile à la page 1046 du paragraphe 36.3.6 STL‐like Operations (quatrième édition publiée en 2013). Sauras‐tu la retrouver ? Voici l’extrait en question :

The replace() replaces one substring with another and adjusts the string’s size accordingly. For example:

void f()
{
 string s = "but I have heard it works even if you don't believe in it";
 s.replace(0,4,"");                   // erase initial "but "
 s.replace(s.find("even"),4,"only");
 s.replace(s.find(" don't"),6,"");    // erase by replacing with ""
 assert(s=="I have heard it works only if you believe in it");
}

A replace() returns a reference to the object for which it was called. This can be used for chaining operations:

void f2()
{
 string s = "but I have heard it works even if you don't believe in it";
 s.replace(0,4,"").replace(s.find("even"),4,"only").replace(s.find(" don't"),6,"");
 assert(s=="I have heard it works only if you believe in it");
}

Pas trouvé ? Pas d’inquiétude, aucun humain n’avait trouvé cette erreur. Bien après la publication de ce livre, cette erreur a été trouvée, non pas par une nouvelle forme d’intelligence artificielle, mais juste par un outil d’analyse statique de code source au nez et à la barbe des pointures C++ aguerries.

Explications

Pour des questions de performance, le standard C++ (avant C++17) indique que c’est le compilateur qui optimise l’ordre d’évaluation du chaînage et des paramètres de fonction. Le standard utilise le terme unsequenced (séquencement non défini). Le C et le C++ partagent ensemble cette règle.

Donc, l’expression replace(find()).replace(find()) dans la fonction f2() peut être évaluée dans des ordres différents. En théorie, la variable s pourrait donc contenir différents résultats. Et c’est aussi le cas en pratique :

Compilateur Résultat contenu par la variable s
GCC et MSVC I have heard it works evenonlyyou donieve in it
LLVM/Clang I have heard it works only if you believe in it

Détails

Ci‐dessous, la première ligne déclare et initialise un objet std::string. La seconde ligne cherche et remplace plusieurs caractères de cette std::string en utilisant le chaînage des fonctions replace :

std::string s = "but I have heard it works even if you don’t believe in it";
s.replace(0,4,"").replace(s.find("even"),4,"only").replace(s.find(" don’t"),6,"");

Intuitivement, on s’attendrait à évaluer les arguments des fonctions comme find("even") juste avant d’appeler replace(resultat,4,"only"). Mais ce n’est pas le cas avant C++17, ces arguments peuvent être évalués dans différents ordres. Plus de détails sont donnés par Shafik Yaghmour (en anglais).

Le tableau ci‐dessous présente sur chacune des sept lignes, un ordre d’appel possible selon les standards C++ (avant C++17) et C (en supposant que ce soit une struct string avec des pointeurs de fonction) :

1ᵉʳ appel 2ᵉ appel 3ᵉ appel 4ᵉ appel 5ᵉ appel
find(" don’t") find("even") replace(0,4,"") replace(f,4,"only") replace(f,6,"")
find("even") find(" don’t") replace(0,4,"") replace(f,4,"only") replace(f,6,"")
find(" don’t") replace(0,4,"") find("even") replace(f,4,"only") replace(f,6,"")
find("even") replace(0,4,"") find(" don’t") replace(f,4,"only") replace(f,6,"")
replace(0,4,"") find(" don’t") find("even") replace(f,4,"only") replace(f,6,"")
replace(0,4,"") find("even") find(" don’t") replace(f,4,"only") replace(f,6,"")
replace(0,4,"") find("even") replace(f,4,"only") find(" do’'t") replace(f,6,"")

C++17 n’autorise qu’une seule possibilité, la dernière du tableau, et correspond à celle de la fonction f() du livre :

s.replace(0, 4, "");
s.replace(s.find("even"), 4, "only");
s.replace(s.find(" don't"), 6, "");

Autres exemples

Par exemple, dans l’expression f().g(h()) la fonction f() peut être appelée avant ou après h(). Le standard C++ fait la différence entre unspecified (non spécifié) et unsequenced (non séquencé). Ce comportement est bien spécifié, donc jusqu’à C++14, c’est unsequenced. À partir de C++17, c’est f() avant h() (sequenced before).

// Avant C++17, f() peut être appelée avant ou après h()
f().g( h() ); 
// C++17 est plus intuitif : f() est toujours appelée avant h()

C’est aussi le cas de l’expression std::cout << f() << g() << h(); dont les trois fonctions peuvent être appelées dans n’importe quel ordre :

// Avant C++17, le compilateur décide l’ordre d’évaluation de f(), g() et h()
std::cout << f() << g() << h() << std::endl; 
// C++17 fixe l’ordre intuitif : d’abord f(), puis g() et enfin h()

Encore d’autres exemples que le C++ partage avec le C :

std::map<int, int> m;
m[0] = m.size();
std::cout << m[0]; // Affiche 0 ou 1 ?
// Clang  : 0
// GCC    : 1
// MSVC++ : 0
int i = 0;
std::cout << i << ' ' << i++; // Affiche 0 0 ou 1 0 ?
// Clang  : 0 0
// GCC    : 1 0
// MSVC++ : 1 0
int i = 0;
i = i++ + 1;    // unsequenced
std::cout << i; // Quelle valeur ?
// Clang   : 1  mais avertit : multiple unsequenced modifications to 'i'
// GCC-6.2 : 1  mais avertit : operation on 'i' may be undefined

Ci‐dessus, pour toutes les versions du C++ et du C, l’opération est unsequenced et non pas undefined, comme GCC-6.2 le laisse supposer :

int i = 0;
i = ++i, i++, i++, ++i, ++i, ++i, i++;
std::cout << i; // Quelle valeur ?
// Piège, toujours 7 car c'est "sequenced before"
// GCC-6.2 avertit : operation on 'i' may be undefined

Notons que GCC-6.2 suppose encore que l’opération est undefined, alors que dans ce dernier cas l’opération est sequenced before, quelle que soit la version du C++ (et même du C).

Conséquence

Donc, de nombreux codes source sont potentiellement truffés de ces pièges, ce qui est également le cas quand std::future<T> est utilisé. Tout le monde se fait avoir, débutants comme experts. Et le comité de normalisation du C++ a donc amendé sans trop discuter ce TS, afin de fixer l’ordre d’évaluation dans certains cas.

Et c’est justement cet exemple du livre The C++ Programming Language qui illustre le paragraphe 5.2.2 Function call (page 107) du standard C++ (brouillon de juillet 2016).

Nouvelle règle

L’évaluation est :

  • de la gauche vers la droite pour les expressions suffixées. Ceci inclut les appels de fonction et la section des membres ;
  • l’affectation de la droite vers la gauche (a = b = c = d) ;
  • les opérateurs de décalage (shift operators) de la gauche vers la droite.

En revanche, lorsqu’une surcharge d’opérateur est invoquée, la priorité arithmétique est utilisée.

Peut‐être que le code généré sera moins performant, et que les standards C et C++ divergent un peu plus, mais, au moins, le langage C++ devient un peu plus intuitif.
¯\(ツ)

Appel à participation

La précédente dépêche a reçu 227 commentaires, soit un volume dix fois supérieur à la dépêche elle‐même. Tous ces commentaires cachent tout de même quelques joyeux trolls velus !

Quand on pense à toute cette énergie dépensée et toutes ces heures consacrées à rédiger ces 227 commentaires ! Avec le recul, nous aurions pu concentrer tout cet investissement dans une dépêche collaborative du style « Aujourd’hui, est‐il pertinent de choisir le C++ pour une nouvelle application ? ».

Panneau « Please Do Not Feed the Trolls » Panneau Troll barré
Ne pas nourrir les trolls Ne pas nourrir les trolls

Mais il n’est jamais trop tard ! Aussi nous proposons vous de rédiger la dépêche « Faut‐il continuer à apprendre le C++ ? » Les nombreux commentaires de la dépêche précédente méritent d’y être copiés. Malheureusement, ceux‐ci sont rarement sous licence compatible CC BY-SA 4.0. Ceci est donc un appel à tous leurs auteurs pour les copier dans cette dépêche afin de la nourrir. Ainsi, nous pourrons les structurer et proposer des réponses concises, claires et utiles à tous.

Merci et à vos claviers !  

Réutilisation

Le texte de cette dépêche est protégé par le droit d’auteur la gauche d’auteur et réutilisable sous licence CC BY-SA 4.0. Les images utilisées sont aussi sous licence libre (cliquer sur l’image pour plus de détails).

Donc, n’hésitez pas à réutiliser ce contenu libre pour créer, par exemple, des supports de formation, des présentations (Meetups), des publications sur d’autres blogs, des articles pour des magazines, et aussi un article C++17 sur Wikipédia dès que Wikipédia passera de la licence CC-BY-SA 3.0 à la CC-BY-SA 4.0 (le contenu de cette dépêche utilise la version la CC-BY-SA 4.0).

Les auteurs

Par respect de la licence, merci de créditer les auteurs :

Continuer à améliorer ce document

Malgré tout le soin apporté, il reste certainement des oublis, des ambiguïtés, des fôtes… Bien que cette dépêche restera figée sur le site LinuxFr.org, il est possible de continuer à l’enrichir sur le dépôt Git du Groupe des utilisateurs C++ francophone (C++FRUG). C’est donc sur ce dépôt que se trouvent les versions les plus à jour.   (ღ˘⌣˘ღ)

La suite

Nous venons de découvrir un changement important au niveau du langage. La dépêche suivante nous dévoilera une autre nouveauté du C++17.

Chère lectrice, cher lecteur LinuxFr.org. Tu souhaites apporter ta pierre à cet édifice ? Rejoins‐nous dans l’espace de rédaction collaborative sur LinuxFr.org (un compte est nécessaire pour y accéder).

À suivre…

Aller plus loin

  • # Génial

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

    Alors ça c'est vraiment un moyen de résoudre des bugs subtiles car beaucoup de débutants ne savent pas que l'ordre est non-défini.

    Surtout que la plupart des livres que j'ai lu n'en parlaient pas.

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

    • [^] # Re: Génial

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

      En effet, il n'y a en général guère que dans les guides qualité que l'on peut trouver des références à ces problèmes. Et encore, en général, on se contente du f[i] = i++;.

    • [^] # Re: Génial

      Posté par  . Évalué à 4.

      Je suis surpris parce qu'on m'avait dit que c'était volontaire, que ça permettait de laisser plus de liberté aux compilateurs, voir que c'était lié à l'architecture matériel et à la manière dont on code l'appel d'une fonction dans l'assembleur donné.

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: Génial

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

        C'est le cas (d'ailleurs les exmples dans la dépêche montrent que certains compilateurs font des choix différents). Mais il y a toujours un compromis entre "on laisse le compilateur faire le truc le plus rapide possible" et "on fait un truc qui fonctionne comme attendu". Par exemple, le comportement du modulo sur les nombres négatifs n'était pas défini en C89, mais il l'est devenu en C99.

      • [^] # Re: Génial

        Posté par  . Évalué à 2.

        En même temps si le compilateur rend un programme vide mais super optimisé alors que le développeur attendais à ce qu'il fasse quelque chose parce qu'il a été super malin en trouvant un bug de la spec qui fait interpréter ton code par un no-op, certes il a eu de la liberté mais toi tu seras pas plus avancé ;)

        • [^] # Re: Génial

          Posté par  . Évalué à 3.

          C'est pour un peu contrebalancer les discours un peu « droit dans mes bottes » sur d'autres aspects : « si ce n'est pas standardisé, c'est que ces problèmes ne pourront sans doute jamais être résolus de manière sûre » source.

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

  • # Correction

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

    s/assignement/affectation/

  • # Erreur de livre et experts C++

    Posté par  . Évalué à 5.

    Pas trouvé ? Pas d’inquiétude, aucun humain n’avait trouvé cette erreur.

    Sérieux ?
    Autant, je ne me serais peut être (voir certainement) pas posé de question car il s'agit en effet du livre de Bjarne, et je me serait naturellement dit que s'il l'écrivait ainsi, il devait être sûr de son coup.

    Mais quand même, quand je vois ça :
    s.replace(0,4,"").replace(s.find("even"),4,"only").replace(s.find(" don't"),6,"");

    En sachant à l'avance qu'il y avait une erreur dans le code, je n'ai pas hésité une seule seconde, et je ne me défini absolument pas comme un expert C++, donc me dire qu'aucun expert ne l'avais vu, ça me surprend énormément !

    Perso, je vois ça dans du code dans la boite où je bosse, le mec qui l'a écrit a droit une courtoise comparaison avec de charmants animaux ayant des pratiques sexuelles réprouvées par la morale et le code et réécrit dans une forme où le doute n'est plus possible illico !

    • [^] # Re: Erreur de livre et experts C++

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

      Oui, c'est sûr, les humains n'auraient pas pensé remettre en cause les écritures saintes du C++.
      Mais les algorithmes ont moins d'état d'âme ;-)

      Commentaire sous licence Creative Commons Zero CC0 1.0 Universal (Public Domain Dedication)

    • [^] # Re: Erreur de livre et experts C++

      Posté par  . Évalué à 2.

      C'est pas sympa d'insulter les gens qui font des erreurs.

      • [^] # Re: Erreur de livre et experts C++

        Posté par  . Évalué à 10.

        Ce n'est pas le fait qu'il y ai une erreur qui l'embête à mon avis, mais plus le fait de chaîner de manière illisible les traitements. Oui c'est illisible, quand on met des années à voir le bug alors qu'il est scruter par un grand nombre de spécialistes du langage.

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: Erreur de livre et experts C++

          Posté par  . Évalué à 9.

          C'est exactement ça, ce qui m'embête, c'est le fait d'écrire du code qui est ambigu et qui laisse le doute à la lecture.
          Surtout que dans le cas présent, ça ne dois pas coûter beaucoup plus cher d'écrire une ligne à la fois !

          Quand tu bosses à beaucoup sur un projet, qui a une base de code énorme sur plus de 20 années d’existence, et des tas de développeurs différents qui sont passés dessus, à un moment il faut limiter les risques !
          Et avoir des pratiques saines limite en effet les risques !

          aucun langage n'est sûr, tu peux toujours écrire des choses qui vont avoir un effet inattendu, mais à un moment, dans un langage comme le c++ qui te permet de faire des choses plus dangereuses que d'autres langages, si en plus tu te mets à écrire ce genre de choses, la catastrophe est assurée !

          • [^] # Re: Erreur de livre et experts C++

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

            Nous sommes tous d'accord avec toi pour dire qu'une ligne aussi peu compréhensible comme celle-là n'est pas une bonne pratique.

            s.replace(0,4,"").replace(s.find("even"),4,"only").replace(s.find(" don't"),6,"");

            L'auteur du livre l'avait donné en exemple juste pour illustrer le chaînage.
            Ce n'est absolument pas pour inciter les développeurs à chaîner toutes les fonctions qui supporte le chaînage.

            Commentaire sous licence Creative Commons Zero CC0 1.0 Universal (Public Domain Dedication)

            • [^] # Re: Erreur de livre et experts C++

              Posté par  . Évalué à 7.

              Je pense aussi qu'il s'agit d'un exemple pour illustrer une propos.
              Mais cela montre bien que la langage est dangereux si on l'utilise sans se poser de questions, car même son auteur, dans un livre qui est sensé faire référence, à commis une erreur !

              Et ce qui me surprends le plus dans le cas présent, c'est que personne n'avait remarqué !
              Je ne jette la pierre à personne, si j'avais du lire ce livre, je ne pense pas que la lecture de cette ligne m'aurais fait bondir, car elle sert seulement d'exemple et n'est pas vouée à piloter les moteurs de la prochaine fusée Ariane 42 !
              Mais une fois encore, je ne me qualifie pas d'expert, et en sachant à l'avance qu'il y a une erreur, je tout de suite vu où et quelle était cette erreur.
              Du coup je suis très surpris qu'aucun vrai expert ne l'ai vu !

              • [^] # Re: Erreur de livre et experts C++

                Posté par  . Évalué à 7.

                Disons qu'utiliser un objet comme paramètre a une méthode qui modifie l'objet en question, et chaîner le tout, c'est un peu chercher les coups.
                Ordre défini ou pas, c'est trop facile de rater quelque chose.

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

        • [^] # Re: Erreur de livre et experts C++

          Posté par  . Évalué à 1.

          Oui c'est illisible, quand on met des années à voir le bug alors qu'il est scruter par un grand nombre de spécialistes du langage.

          Et c'est même pas un humain qui l'a trouvé ce bug, c'est une machine ^

    • [^] # Re: Erreur de livre et experts C++

      Posté par  . Évalué à 5.

      C'est sûr, alors qu'une bonne expression régulière aurait fait des miracles en un seul appel.
      Ceci dit, le pattern Fluent est vraiment agréable à utiliser (pour ceux qui font du JQuery, lodash, etc… par exemple, impossible de penser autrement maintenant).

      Le vrai problème du code ci dessus vient de la signature de la fonction replace qui modifie la chaîne au lieu de retourner une nouvelle string. Si cela avait été le cas, alors l'ordre d'appel, on s'en balance. Avec un replace immutable, alors il faut, logiquement faire la recherche de la sous chaîne par la fin pour garantir que les modifications soient appliquées au bon endroit.

      Mais au final, on retombe encore sur les signatures bancales de la classe string, qui ne propose qu'un nombre limité de méthodes (au nom d'une simplification de l'implémentation STL, mais qui, en pratique, force chaque projet à réimplémenter les fonctions de bases avec tous les bugs que ça impliquent). Un string replace(const string & this, const string & by_that) serait alors bien plus simple et dans la majorité des cas (le reste, c'est faisable par un erase + insert). De même pour tout ce qui est basé sur des index en fait, dans la majorité des cas, c'est inutile et vraiment pénible dès lors que l'on fait du UTF-8.

      Un code comme ceci est bien plus compréhensible:

      String s = "The blue cat doesn't like the yellow dog";
      return s.replace("yellow", "blue").replace("blue", "yellow").replace("dog", "cat").replace("cat", "dog");
      // Autre exemple, plus concret
      String get_authority(const String & url) {
      // Exemple: url = "http://whatever.org/path/to/index.html";
      return url.from_first("://").to_first("/");
      }

      Le deuxième exemple, montre que, même si en interne la classe utilise des index pour trouver les sous chaînes, ils ne sont pas visibles dans la signature des méthodes, et donc c'est carrément plus intuitif. Côté performance, avec du COW, c'est mieux que du code à base de find & replace, vu qu'il ne faut plus "sortir" les index de la stack dans les fonctions supérieures, une seule fonction appelée, moins de registres utilisés et donc, au final, tourne beaucoup plus vite.

      • [^] # Re: Erreur de livre et experts C++

        Posté par  . Évalué à -1.

        Ceci dit, le pattern Fluent est vraiment agréable à utiliser

        Le problème n'est pas l'API fluent, écrire ça :

        auto even = s.find("even");
        auto dont = s.find(" don't");
        s.replace(0, 4, "")
         .replace(even, 4, "only")
         .replace(dont, 6, "");

        Ne pose aucun problème.

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: Erreur de livre et experts C++

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

          Sauf que ça marche pas non plus!

          Tu as reproduit le même bug: le premier replace déplace tous les caractères, et tes offsets (even et dont) ne sont plus au bon endroit sur la nouvelle chaîne.

          • [^] # Re: Erreur de livre et experts C++

            Posté par  . Évalué à 5.

            Effectivement, je me suis raté.

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: Erreur de livre et experts C++

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

        Pas besoin de payer pour du COW. Le déplacement marcherait très bien si on travaille sur une chaine sans changer sa taille.

        Maintenant, dans "classe string, qui ne propose qu'un nombre limité de méthodes", je lis : "rajoutons des fonctions membres à std::string". Pitié non il y en a déjà bien assez dedans, et pas assez dehors.

        Résultat avec l'arrivée des string_view on va se payer une surcharge de trucs déjà écrits, ou l'impossibilité d'utiliser ce boost de performance et d'API unifiée. C'est bien idiot. Je vois régulièrement des projets qui n'ont rien trouvé de mieux à faire que de créer leur propre classe de chaine juste pour rajouter des fonctions membres dedans au lieu de les fournir en libre. Résultat ils écrivent des choses dont ne on pourra pas profiter sur les string_view—ou des équivalentes que l'on pourrait vouloir écrire.

        • [^] # Re: Erreur de livre et experts C++

          Posté par  . Évalué à 6.

          C'est typiquement l'un des meilleurs argument au duck/structural typing :)

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: Erreur de livre et experts C++

          Posté par  . Évalué à 4.

          C'est marrant, j'ai exactement l'avis opposé. Je trouve beaucoup plus intuitif de faire obj.methode() pour une action que func(obj).
          Mon commentaire, n'était pas pour ajouter des méthodes à std::string, mais pour dire que l'interface initiale est pourrie, car trop basée, comme pour les premières classes de la STL, sur des idées 1980 (code ASCII 8bits, tout le monde parle anglais, etc…), et pas assez sur l'utilisation de l'objet lui-même. Dans la réalité des codes utilisant du std::string que j'ai lu (et j'en lis beaucoup), 99.9% du temps, les indices sont inutiles, plein de bugs, etc…

          L'exemple le plus flagrant, c'est lorsque je dois faire l'i18n d'une appli C++. Le développeur ne sait pas (ou a "oublié") qu'il travaillait avec de l'UTF-8, et estime, à tort que l'index d'un caractère est égal à sa position en mémoire. Avec une interface sans index à gérer, il n'y a aucun soucis. Avec std::string, c'est l'horreur. Avec une classe utilisant le pattern "from_first", "upto_last", etc, ça roule tout seul.

          Traduire le code des iostreams c'est aussi une autre hérésie qui n'aurait jamais dû survivre à plus d'un standard. Le grand classique, que j'ai vu à de nombreuses reprises, c'est "cout << "There is " << animal_count << " pet" << animal_count ? 's' : '\0' << " in the store."<< endl;
          En général, là, le code est à réécrire (grand moment de solitude). Franchement, séparer la présentation des données, même le C le faisait en 89 avec printf et gettext, le C++ n'y est toujours pas (il y a boost::format mais bon… boost quoi…).

          De plus, l'interface des string aurait due être séparée en "méthodes" non modifiantes, "méthodes modifiantes", quitte à faire une classe "readonly_string" qui permette les recherches, les modifications de taille (ou du pointeur de début de chaîne) mais pas du contenu de la chaîne (bref: tout ce qui est requis pour parser du texte), et une classe "writeable_string" pour le reste, en ayant un héritage multiple des deux interfaces dans ce qui s'appelle la classe string. Du coup, la problématique du COW disparaît automatiquement suivant que l'on veuille en payer le prix ou non (pour le parsing, par exemple, pas de COW pour du RO).

          • [^] # Re: Erreur de livre et experts C++

            Posté par  . Évalué à 2.

            C'est marrant, j'ai exactement l'avis opposé. Je trouve beaucoup plus intuitif de faire obj.methode() pour une action que func(obj).

            C'est la même chose, mais les IDE peuvent t'aider à chercher avec la notation pointée.

            Mon commentaire, n'était pas pour ajouter des méthodes à std::string, mais pour dire que l'interface initiale est pourrie[…]

            Le fait de ne pas avoir d'interface ne doit pas aider…

            L'exemple le plus flagrant, c'est lorsque je dois faire l'i18n d'une appli C++.

            Oui et d'ailleurs pas forcément en UTF-8, hein ? Ça peut aussi être de l'UCS2 par exemple.

            Traduire le code des iostreams c'est aussi une autre hérésie[…]

            C'est joli mais pas beau peut être qu'en ajoutant un opérateur % pour faire :

            cout << "There is %s pet in the store" % animal_count;

            Pourrait être mieux

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

          • [^] # Re: Erreur de livre et experts C++

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

            De plus, l'interface des string aurait due être séparée en "méthodes" non modifiantes, "méthodes modifiantes", quitte à faire une classe "readonly_string" qui permette les recherches, les modifications de taille (ou du pointeur de début de chaîne) mais pas du contenu de la chaîne

            On est en C++ là. Il suffit de faire une const std::string. Les méthodes que tu peux utilisées sont alors uniquement celles annotées "const", qui ne modifient pas le contenu de l'objet. Pas besoin d'une deuxième classe.

            Cela dit, il me semble qu'il y a dans les projets pour C++17 une "string_view", qui ne stocke pas ses données mais permet de faire ce genre de choses avec des données externes (venant d'une string, d'un char*, ou d'une autre string_view), ceci afin de limiter le nombre de copies des données.

          • [^] # Re: Erreur de livre et experts C++

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

            C'est marrant, j'ai exactement l'avis opposé. Je trouve beaucoup plus intuitif de faire obj.methode() pour une action que func(obj).

            Je suis de plus en plus de l'avis que ce n'est que du sucre syntaxique. Sucre qui de par sa forme bride le polymorphisme sur un seul argument. Dans un monde où l'on aurait des multimethods (Stroustrup et d'autres y travaillent, pour après le C++17 donc), çà donnerait (o1,o2).method avec la syntaxe point, mais d'autres langages ont choisi [message o1 o2] (ou des trucs approchants que je peux retrouver).

            Bref, je regrette un peu que l'Unified Call syntax n'ai pas été retenue pour le C++17: https://isocpp.org/blog/2016/02/a-bit-of-background-for-the-unified-call-proposal

            Et quid des types natifs? Dans une fonction générique, on pourrait écrire juste trim(machaine) que machaine soit une std::string, une std::string_view, une QString, une CString, une ossimString… ou tout simplement un tableau char(&)[N], ou encore un pointeur sur chaine 0-terminée char*. Avec l'écriture point, on perd les deux dernières possibilités.

            [UTF-8]

            Entièrement d'accord qu'il y a plein de soucis, même avec boost.locale (surcouche C++ à ICU), cela reste assez peu intuitif à manipuler.

            chaines constantes

            Ce que tu décris ressemble à std::(experimental::)string_view. C'est justement la raison pour laquelle je suis persuadé que l'extension d'interface par l'extérieur (on retrouve un des items de (More?) Exception C++ de Sutters—dispo sur http://gotw.ca/gotw aussi) est préférable.

            Sur les Cpp Core Guideline, on retrouve les mêmes principes avec les span. Et on voit vite que l'on n'a pas besoin d'héritage, que c'est simple, que cela ne nécessite pas d'écrire des fonctions templates, et que c'est aussi efficace que le couple pointeur+taille du C.

            Sinon, ce sujet me rappelle la profusion de classes pour les chaines de caractères dans Adobe.ASL. Là, on a toujours un outil ad'hoc, mais c'est plus complexe pour le pékin moyen que nous sommes qui préfère un seul type pour les unir toute, à la QString.

            D'accord aussi que produire uniquement des nouvelles chaines (à la mode fonctionnelle: i.e. pas d'altération d'état) aurait pu être pal mal du tout.

            [flux]

            Pour les flux, oui, c'est une catastrophe pour l'i18n. Sur le sujet, je teste spdlog et la bibliothèque de formattage sous-jacente en ce moment.

    • [^] # Re: Erreur de livre et experts C++

      Posté par  . Évalué à -1.

      bah putain, donne moi le nom de ta boite, que je n'y postule jamais !

      • [^] # Re: Erreur de livre et experts C++

        Posté par  . Évalué à 5.

        ? C'est pourtant pas bien compliqué, ni mauvais, de ne pas faire de train wrecks !

        Mais je suis d'accord pour que tu n'y postules jamais, si c'est pour écrire du code aussi difficile à lire. j'ai déjà eu trop de boulot qui consistait à "passer derrière les porcs". :(

        "Quand certains râlent contre systemd, d'autres s'attaquent aux vrais problèmes." (merci Sinma !)

      • [^] # Re: Erreur de livre et experts C++

        Posté par  . Évalué à 4.

        Ne t'inquiète pas, si tu écris ce genre de choses, quand bien même tu y postulerais par erreur ou inadvertance, il y a peu de chances pour que tu y sois embauché.

    • [^] # Re: Erreur de livre et experts C++

      Posté par  . Évalué à 4.

      Perso, je vois ça dans du code dans la boite où je bosse, […]

      Je trouve aussi que l'exemple est assez mal choisi, on doit rarement rencontrer ce genre d'enfilades en C++.

      Par contre, je suis déjà tombé sur ce même problème, mais avec des stream, pour lesquelles enfiler des fonctions/entrées est l'usage:

      std::cout  << obj.fy() << "\t" << obj.fx() << " -> " << obj.result() <<std::endl;

      Où les appels des premières méthodes affectent le résultat, qui sera donc différent selon l'ordre d'exécution.

      De mémoire MSVC évaluait de droite à gauche alors que GCC et CLang évaluaient de gauche à droite.

      • [^] # Re: Erreur de livre et experts C++

        Posté par  . Évalué à 6.

        Exemple:

        class obj
        {
        public: 
            obj():_r(1){}
            ~obj(){}
            unsigned int fx(){return ++_r;}
            unsigned int fy(){return (_r*=_r);}
            unsigned int result(){return _r;}
        private:
            unsigned int _r;
        };
        int main(int argc, char* argv[])
        {
        
            obj o;
            std::cout << o.fx() << '\t' << o.fy() << "\t" << o.result() << std::endl;
            return 0;
        }

        Visual studio 2012: 2 1 1
        Clang 3.4.1 : 2 4 4


        je n'ai pas de GCC sous la main

        • [^] # Re: Erreur de livre et experts C++

          Posté par  . Évalué à 5.

          Avec gcc (Debian 4.9.2-10) :

          2   1   1
          

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

          • [^] # Re: Erreur de livre et experts C++

            Posté par  . Évalué à 2.

            En effet.
            - Il me semble pourtant avoir obtenu 2 4 4 avec un GCC. -

            Ceci dit, le développeur qui a écrit ce code, ainsi que tous ceux qui l'on lu et qui ont cherché d'où venait la boulette , s'attendait à avoir comme résultat 2 4 4.

  • # Faut-il continuer à apprendre le C++ ?

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

    non

    • [^] # Re: Faut-il continuer à apprendre le C++ ?

      Posté par  . Évalué à 4.

      Ici une réponse laissée indéfinie par le standard et dépendant du bon vouloir du compilateur (oui, non, 42, -32768, dépassement d'addition signée, NaN).

    • [^] # Re: Faut-il continuer à apprendre le C++ ?

      Posté par  . Évalué à 5.

      C++ c'est nul parce qu'il y a des ambiguïtés, à la place il faut apprendre javascript, parce que tout le monde peut deviner naturellement ce que font par exemple 0+'' ou si undefined+0+'' est égal à undefined+''+0 ou non.

  • # Licence

    Posté par  . Évalué à 4.

    Malheureusement, ceux-ci sont rarement sous licence compatible CC BY-SA 4.0.

    J'en profite pour modifier ma signature si ça peut simplifier la vie à certains :)

    Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Licence

      Posté par  (site web personnel, Mastodon) . Évalué à 1. Dernière modification le 01 décembre 2016 à 12:02.

      Il te manque un T à "mes écrits".

      (et si vous voulez réutiliser mes commentaires pour la prochaine dépêche, pas de problème)

      • [^] # Re: Licence

        Posté par  . Évalué à 1.

        Corrigé ! Merci :)

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Licence

      Posté par  . Évalué à 2.

      Et si vous voulez réutiliser ma signature, y'a pas de problème.

      Cette signature est publiée sous licence WTFPL

  • # Coquille

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

    c’est f() avant g() (sequenced before).

    Je pense que c'est c’est f() avant h() (sequenced before)

    Sinon excellent journal, le dessin m'a bien fait marrer !

Suivre le flux des commentaires

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