URL: https://linuxfr.org/news/faut-il-continuer-a-apprendre-le-c Title: Faut‐il continuer à apprendre le C++ ? Authors: Oliver Davy Defaud, gbdivers, Ontologia, Nicolas Boulay, abriotde, freem, lmg HS, Benoît Sibaud, Snark, Guillaum, khivapia, barmic, gusterhack, whity, Cilyan Olowen, palm123, esdeem, Nils Ratusznik, ZeroHeure, bayo, thoasm, Anthony Jaguenaud, alkino, baobab, chicco, lolop, martoni, jc, Pascal Obry, nnamrok, Jona, wilk et lovasoa Date: 2016年11月29日T23:16:54+01:00 License: CC By-SA Tags: développement et fortran Score: 58 Le C++ est un langage qui a vu ses spécifications s’amonceler et se stratifier au cours des années. Il est encore très utilisé. Mais face à des concurrents comme Rust ou Go, quelle est sa place dans un environnement qui évolue ? Cette dépêche qui a nécessité beaucoup de discussions, aura, vous le verrez, une forme un peu particulière. ---- ---- Origine de cette dépêche ======================== La dépêche [*C++17, genèse d’une version mineure*](https://linuxfr.org/news/c-17-genese-d-une-version-mineure) a reçu [227 commentaires](https://linuxfr.org/news/c-17-genese-d-une-version-mineure#droit-dauteur-licences-remerciements) représentant un volume dix fois supérieur à la dépêche elle‐même. Et ces commentaires, très souvent bien écrits, nourrissent des [trolls](https://fr.wikipedia.org/wiki/Troll_%28Internet%29) bien velus ! L’idée de créer cette dépêche vient de deux constats : 1. l’énergie dépensée et les nombreuses heures consacrées à rédiger ces 227 commentaires est phénoménale ; 2. en revanche, la lecture de tous ces commentaires est laborieuse. 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 ? », afin de structurer et consolider tous les arguments dispersés au fil des commentaires. Objectif de cette dépêche ========================= Cette dépêche propose un sujet bien plus large : « Faut‐il continuer à apprendre le C++ ? ». Et de nombreux commentaires de qualité de la dépêche [*C++17, genèse d’une version mineure*](https://linuxfr.org/news/c-17-genese-d-une-version-mineure) méritent d’être copiés, améliorés ou fusionnés ici. Malheureusement, ceux‐ci sont rarement sous licence compatible CC-BY-SA 4.0. > **gbdivers** proposition Faire un article synthétique sur cette question n’est pas simple. En effet, le choix d’un langage pour un projet dépend énormément du contexte, en particulier le domaine d’application et l’environnement technique existant. Dans les discussions sur le choix d’un langage, certains avanceront des critères, mais ceux‐ci seront considérés par d’autres comme non pertinents. Même des critères « historiques » du C++ ne sont plus des arguments suffisants dans de nombreux projets. Par exemple, le C++ est souvent évoqué pour ses performances lorsque l’on veut créer un jeu vidéo. Mais de nos jours, avec la puissance disponible sur le moindre appareil et la qualité des moteurs de jeux, il n’est plus pertinent que tous ceux qui veulent travailler dans les jeux vidéo apprennent le C++. De même, si l’on prend un domaine dans lequel le C++ est généralement déconseillé, comme le développement Web. Et, cependant, de très grosses entreprises Web comme Google ou Facebook utilisent le C++. Il convient donc de revenir dans un premier temps sur les critères à prendre en compte dans le choix d’un langage, puis de voir les qualités et défauts du C++ sur ces critères. Dialogue sur divers aspects des enjeux brulants de C++ en 2018 ============================================================== La normalisation, un processus trop lent ? Et _quid_ de la nouvelle norme ISO _vs_ mise à jour d’un logiciel (compilo, VM, etc.).> **Nicolas Boulay** : Vu la base de code existante et vu le nombre de parties prenantes, une prise de décision est forcément lente, vu les enjeux par chacune des parties. De toute façon, ce n’est pas vraiment la rapidité des changements de la norme qui sont impactants, c’est la vitesse de conformité des compilateurs.> Si c’est lent, les fonctionnalités vont prendre du retard avec les langages comme C#, Rust, etc., surtout s’ils continuent à avancer. Si c’est rapide, il faut accompagner le changement, pour ne pas dérouter les codeurs. Il y a risque de ne pas utiliser les nouveautés par peur d’instabilité, d’immaturité du code, ou de méconnaissance.>> **gbdivers** : Oui, je suis d’accord. À mon avis, c’est avec ce type d’arguments que l’article sera intéressant. Les lecteurs ne connaissent pas forcément le contexte et les contraintes du C++, donc c’est bien de donner le contexte, puis les avantages et défauts des choix possibles. Libre à eux ensuite de se faire leur propre opinion.>> **Anthony J.** : Les industriels. Je pense que pour de nombreux industriels utilisant le C++ depuis plus de vingt ans, les besoins de rétrocompatibilité restent très importants.>> **gbdivers** : Bonne question. Ont‐ils besoin de rétrocompatibilité sur les évolutions du C++, ou ont‐ils simplement besoin de ne pas suivre les évolutions ? On a au moins deux choix possibles : le choix entre suivre les évolutions du langage et mettre à jour son code, ou ne pas mettre à jour son code et ne pas suivre les évolutions du langage. Faut‐il avoir un troisième choix : suivre les évolutions et ne pas mettre à jour son code ? Est‐ce que ce troisième choix est intéressant ou pertinent pour les industriels ? Pour le C++ ?> Un exemple concret : dans ma boîte, ils ont choisi il y a trois ou quatre ans de mettre à jour le code et de ne plus assurer la rétrocompatibilité. Et, depuis, on est sur le C++11 (il n’est pas prévu que l’on passe au C++14 ou 17 pour le moment). Et l’on garantit la compatibilité du code sur une liste définie de compilateurs et de versions de compilateurs. C’était un choix stratégique à faire entre obliger nos clients à mettre à jour leur code (et donc potentiellement perdre des clients) ou continuer à assurer la compatibilité (avec les coûts de développement que cela implique). **gbdivers** : Standard _vs_ lib ? Pourquoi ne pas mettre toutes les fonctionnalités dans le standard. Exemple de regex, network, Unicode. > **Nicolas Boulay** : Une uniformisation serait souhaitable .>> **gbdivers** : Idem. Quels sont les avantages et défauts d’une uniformisation ? D’une standardisation ? D’une standardisation ISO ? Vaut‐il mieux une fonctionnalité dans le standard ou dans une lib ? Quelles sont les conséquences de l’un ou l’autre choix ?>> **Nicolas Boulay** : L’uniformisation permet tous les avantages de la standardisation. Si l’on parle de la lib standard, d’un point de vue utilisateur cela ne change rien.>> **gbdivers** : L’antithèse est que l’uniformisation peut figer (dans le sens péjoratif, c.‐à‐d. bloquer les évolutions futures) une API ou des fonctionnalités. Prenons par exemple le protocole HTTP. Si le standard C++ fournit une API pour cela, il faut que l’API puisse suivre les évolutions. Sinon, s’il y a une mise à jour de HTTP, soit on continue à utiliser l’API pas à jour du C++, soit on utilise une API à jour fournie par une bibliothèque. Dans les deux cas, c’est problématique de fournir une API figée dans le standard. Il faut que l’API soit indépendante de la version du protocole, de façon à ce que les compilateurs puissent fournir des mises à jour du protocole sans que le standard ne change.>> **freem** : Il y a aussi le problème de la rétro‐compatibilité. Par exemple, admettons que l’on intègre (reproche régulièrement lu et entendu) une section pour créer des IHM graphiques. Ce que le développeur voudra, c’est un truc qui soit stable au niveau UX **et** intégré dans les systèmes d’exploitation (Windows, macOS, GTK+, Qt...). Vu que celles‐ci évoluent au fil du temps, tant dans leurs API que dans leurs architectures, le risque d’avoir une partie du standard qui soit instable (au niveau API du moins) serait élevé. **gbdivers** : Les nouveaux langages sont‐ils meilleurs que les anciens langages ? De grands débats apparaissent : `const` _vs_ `mutable`, ref _vs_ copie, `explicit` _vs_ `implicit`.> **Nicolas Boulay** : Il ne faut pas oublier l’apport de la recherche dans le domaine : le temps de dev directement lié aux nombres de lignes de code (o(L^1.2 ), _cf._ Cocomo). La liste des bogues les plus fréquents en C et C++ qui sont toujours les mêmes (NULL, _off by one_, débordement de tampon, allocation et désallocation mémoire, mauvaise gestion d’erreur, etc.), et qui n’existe pas dans les langages plus récents.> **gbdivers** : > L’argument sur les avantages que peut apporter un nouveau langage est à rapprocher du coup au problème de rétro‐compatibilité.> Je ne connais pas Cocomo.> **freem** : Cocomo est une méthode qui consiste à chiffrer le coût d’un projet en fonction de son nombre de lignes de code. C’est, euh, pas très moderne (né en 1970) et, de ce que j’en sais, totalement bancal : en C++, il est possible d’utiliser de l’objet, l’encapsulation est très encouragée par les mécanismes (RAII, _move semantic_, généricité) et donc, pour contribuer à un code C++ « idéal », le coût peut être très faible malgré un grand nombre de lignes de code, tant que l’on sait dans quelle couche on se situe. Par exemple, pour un bogue de mémoire (erreur de segmentation, débordement de tampon) il est peu pertinent d’attaquer le code haut niveau, qui peut représenter le plus gros (avec plein de règles métier ajoutées au fil du temps). Personnellement, de ce que je sais, la [complexité cyclomatique de McCabe](https://fr.wikipedia.org/wiki/Nombre_cyclomatique), dont j’ai personnellement tirée pas mal de leçons, me semble plus pertinente que le nombre de lignes pour estimer la complexité d’un code (je vous renvoie à l’outil _cccc_, présent dans toutes les bonnes distros). Le problème des _underflows_ et _overflows_ pourrait être contrecarré en encapsulant les types natifs dans des classes. Le problème, c’est que le coût à l’exécution serait non négligeable. Pour la constance, je rappelle que, par défaut, les paramètres d’une fonction en C++ sont invisibles par l’appelant : passage par copie, sauf si l’on passe une référence. Du coup, on choisit volontairement un type mutable (dont c’est le rôle). Quand on a besoin d’une référence constante, c’est en fait un contournement dû au fait que la copie est lente. Ce qui est, d’ailleurs, bien moins vrai depuis 2011. Ici encore, comme pour les _flow errors_, il serait possible d’implémenter une solution plus propre à base de pointeurs intelligents (_weak_ptr_ dédié à _unique_ptr_, qui pourrait interdire l’altération de la variable cible).> Ici, je dirais que les erreurs les plus communes du « C/C++ » sont dues au fait que, en C++, on a tendance à programmer comme en C, comme le montre si bien la fusion de tant de gens : « C/C++ »... **gbdivers** : Peut‐on envisager de casser la rétrocompatibilité ? L’exemple du Python 2 _vs_ Python 3 montre l’étendue des problèmes que cela implique...> **Nicolas Boulay** : À ne surtout pas faire, cela revient à créer un nouveau langage (sauf à être compatible binaire, avec un drapeau dans le fichier, comme _hhack_ de Facebook).> **gbdivers** : Idem, prise de position. Si on le fait, il y aura telles conséquences. Si on le fait pas, il y aura telles autres conséquences.> **Nicolas Boulay**: Avec un drapeau, vous gardez la rétrocompatibilité, et vous nettoyez en même temps le langage pour le futur. Si vous cassez la compatibilité, vous inventez un nouveau langage, il faut donc convaincre à migrer. Ce qui ne se fera pas. Pour migrer à un nouveau langage, il faut de sacrée bonnes raisons. C++ _n-1_ sera toujours « _good enough_ » pour beaucoup.> **gbdivers** : Pour le drapeau, je sais pas trop. Toujours est‐il que je suis d’accord. Je pense que l’exemple de Python 2 _vs_ Python 3 (et, plus précisément, la lenteur d’adoption de Python 3) est un bon exemple concret que ne pas assurer la rétro‐compatibilité du code peut ralentir l’adoption de la mise à jour.> **freem** : Compte tenu de la vitesse d’évolution du C++, si l’on commence à casser la compatibilité, les compétences et le code accumulés entre deux versions seront potentiellement perdus. Le coût de formation pour avoir un développeur C++ compétent est déjà élevé, si en plus il faut le réinvestir régulièrement juste sur une « lubie » du comité, on risque surtout de voir un retour du C, ce qui serait à mon humble avis une terrible régression (le C a bien moins de fonctionnalités, tout en offrant des performances comparables et moins de sécurité).> Pour parenthèses, l’obsolescence d’`auto_ptr` a dû être faite dans la douleur et dans le constat que c’était de toute façon peu utilisé (il serait intéressant de « grepper » `auto_ptr` dans la base de code des paquets Debian, par rapport au nombre de projets écrits en C++, je suis certain que c’est ultra‐minoritaire. Personnellement, je n’ai souvenir que d’un seul projet qui l’ait utilisé, et je ne me souviens même plus du nom...). J’ai un peu plus de doutes sur l’obsolescence des _bind1st/2nd_ qui, bien qu’horribles à utiliser, ne me semblent pas avoir de raisons liées à des bogues. **gbdivers** : Complexe ? Oh oui ! Multiplication des syntaxes, syntaxes pas forcément intuitives.> **Nicolas Boulay** : Nécessité d’une vérification des bonnes pratiques.> **gbdivers** : C’est un point auquel je n’avais pas pensé et qui serait intéressant d’aborder. Le C++ n’est pas simplement un langage, c’est un écosystème, qui doit être considéré dans son ensemble : compilateurs, bibliothèques, outils de vérifications, bonnes pratiques, apprentissage.> **freem** : La compatibilité avec le C est l’un des arguments forts pour pousser le C++ et la prise en charge des syntaxes du C est nécessaire pour cela (je pense surtout aux _casts_). Ceci dit, les bons compilateurs sont capables de prévenir de ce genre de choses. Il est possible de convertir partiellement en C++ un code C qui, par défaut, n’offre **strictement aucun contrôle automatique de la bonne gestion d’une ressource**, ce qui réduit les temps et les coûts de migration de C vers C++ de façon drastique. Mais il est vrai qu’il n’existe que trop peu d’outils et de documentations claires (leur étant liés) favorisant la vérification automatique des bonnes pratiques, ou même d’un simple _coding style_. On peut cependant regretter qu’il soit difficile de s’assurer que son code n’utilise pas par erreur une fonctionnalité que l’on a voulu bannir pour différentes raisons (les exceptions, les _templates_, la RTTI, etc.). **gbdivers** : Passage complexe entre langages.> **freem** : Si l’on parle d’utiliser plusieurs langages dans un projet, justement, c’est bien plus simple en C++ qu’en C# ou Java : le langage commun utilisé étant le C, _a priori_, et le C++ étant compatible avec une grande partie du C, il n’y a pas besoin de définir des points d’entrée comme en Java. J’ai un très mauvais souvenir également de l’usage de C en C#, où il faut concrètement lutter pour que le compilateur accepte de compiler du code « _unsafe_ ».> En C++, il suffit d’encapsuler la ressource, ce qui est très simple grâce à, je me répète, la RAII.> En revanche, si l’on parle d’apprendre le C++ en venant d’un autre langage, c’est vrai que ce n’est pas trivial, surtout si l’on veut jouer avec les couches basses. **gbdivers** : L’apprentissage du C++ est réputé difficile...> **lmghs** : J’imagine qu’on liste ici les trois niveaux : historique (mémoire à la main, pays magique où les erreurs n’existent pas), 98-RAII (que je continue à appeler moderne, si si), 11-RAII (où l’on introduit les trucs orientés utilisateur et « pythonisants » dès le début : _auto_, _for-range-loop_, _lambdas_ pour algorithmes standards...).> Là, on rejoint la problématique de ce que l’industrie attend. Je suis encore en train de donner une formation cette semaine, et savoir si les formés vont travailler sur une CentOS 6, ou pire 5, plutôt qu’une 7, guide ce que je peux leur enseigner pour répondre à leurs besoins immédiats. Ce qui ne m’empêche pas de faire du _teasing_ en revanche. Dans un cas, je vais parler d’`auto_ptr` et des `boost::ptr_vector`, tandis que dans l’autre, `vector>` me suffit. Même problématique avec les projets en maintenance.> Il y a aussi la question des publics entre un dev futur informaticien, un gars en embarqué, un scientifique ou des gars en formation interne dans une SSII, les choix et possibilités ne sont pas toujours les mêmes. **gbdivers** : Conclusion : est‐ce que le C++ est fini ? Non !> **gbdivers** : En fait, on peut même répondre à cette question (et à la question du titre) dès l’introduction. Pour directement mettre au clair les trolls : est‐ce que quelqu’un pense que le C++ aura disparu dans dix ans ? La réponse est non (même les trolls pourraient difficilement répondre oui a cette question. Même le [[COBOL]] est encore utilisé). Le C++ en bref ============== Le C++ est un vieux langage (bientôt 40 ans) hérité du langage C avec lequel il entretient une compatibilité.> **gbdivers** : Compatibilité de syntaxe, mais divergence dans la façon de concevoir les choses. Cela rejoint un des points donnés plus haut : le passage entre plusieurs langages. Quand un dev connaît et utilise plusieurs langages, il ne maîtrise pas les spécificités de chaque concept dans chaque langage (un même concept peut avoir plusieurs interprétations proches, mais pas exactement identique). > Par exemple, pour le C _vs_ C++, il y a des syntaxes communes, mais la façon de penser un code est complètement différente (gestion des ressources, des erreurs, etc.). Cela a donc un coût de passer d’un langage à un autre. > **Anthony J.** : La compatibilité de syntaxe n’est plus possible depuis déjà pas mal de temps : conversion pointeur → `void*` OK (c/c++) ; mais `void*` → `type*` OK(c)/KO(c++), les tableaux de taille dynamique à l’exécution (C99).> La fonction foo() => foo(...) en C, mais foo(void) en C++.> Un article sur le sujet [_ici_](http://nicolasj.developpez.com/articles/incompatibilites/).> **gbdivers** : Je n’ai pas suivi le C11, si ces syntaxes sont encore valides ? _A priori_, le C++11 (ou 14 ?) est censé se baser sur le C11, donc il est possible que ce ne soit pas compatible avec le C99. Mais, dans ce cas, le C actuel n’est pas non plus compatible avec le C99.> Du coup, au lieu de dire « il entretient une compatibilité », il faut préciser que la compatibilité syntaxique est limitée et la compatibilité « conceptuelle » diverge complètement.> **freem** : La compatibilité est limitée _de facto_ par le fait que le C++ réserve plus de mots que le C. Un code en C peut, par exemple, très bien utiliser `this`, mais un tel code ne pourra pas compiler en C++ (mot réservé pour désigner l’instance de l’objet dont on appelle la méthode, le cas échéant). Il me semble qu’il existait d’autres points, mais je ne m’en souviens pas. De tout ce long historique, le C++ a évolué du mieux possible entre paradigmes de programmation et nouveaux besoins. Au final, sa syntaxe évolue lentement, est complexe, bourrée de subtilités et lente à compiler.> **gbdivers** : Aborder le problème de l’enseignement du C++ ? Le C++ est permissif, il y a de multiples syntaxes pour faire la même chose. Beaucoup de cours essaient d’apprendre plusieurs syntaxes — en commençant par les plus obsolètes, en général — sans donner de hiérarchie d’importance à l’apprenant. Résultat : complexité à comprendre et moins bonne maîtrise en profondeur).> **freem** : Au sujet de la lenteur de compilation, le problème est‐il vraiment lié au langage, ou à l’implémentation de la STL et du compilateur ? Je me rappelle très bien le jour ou j’ai découvert [[Clang]] : la mémoire consommée par une compilation a été divisée par deux et les temps de compilation du projet sur lequel je travaillais par trois. Je n’ai pas fait de tests de vitesse (moins besoin, meilleur matos) entre la STL de GCC et celle de Clang, mais il est clair que ces derniers incluent beaucoup moins de fichiers, l’arborescence et les chaînes d’héritages sont bien moins complexes. J’ai vu au fil des années pas mal de messages sur le Net indiquant que GCC compilait également plus lentement que Visual C++. Alors ce problème de lenteur, ne serait‐il pas dû, au final, à un problème de qualité de GCC (passage à enlever, sauf si publication un _trolldi_, hi hi) ? Concrètement, GCC utilisait un ramasse‐miettes en C et, il y a quelques années (après ma découverte de Clang en tout cas), on a parlé de réimplémenter certains passages en C++, justement pour réduire la nécessité de ce ramasse‐miettes. Mais c’est aussi un langage permettant des optimisations de folie (_costless abstraction_). Le C++ est utilisé au quotidien, ne serait‐ce que la plupart des environnements graphiques et des navigateurs Web (pour flâner sur *LinuxFr.org*). Et le C++ continuera pour encore de nombreuses années à être utilisé en production, notamment pour les applications critiques. Le C++ n’est pas mourant. Au contraire, le C++ connait un vif intérêt depuis C++11. De plus en plus de personnes s’investissent à améliorer davantage ce langage, comme sur la vitesse de compilation qui devrait être grandement améliorée avec C++20. Alors, faut‐il tout effacer et recommencer un nouveau langage qui, par *design*, prend en compte les contraintes de concurrence (multicœur, NUMA) et de montée en charge (_scalability_) ? Des langages de programmation tentent d’obtenir des performances similaires avec une syntaxe expressive et concise.> **freem** : Inventer un nouveau langage _from scratch_, ne serait‐ce pas un peu comme reprendre un projet _from scratch_ ? On perd l’historique, ce qui peut être bien (plus simple de faire une meilleure architecture) et mal (on perd les correctifs de bogues). La puissance du C++ =================== Les points positifs qui démarquent le C++ sont sa programmation générique et sa méta‐programmation qui permettent d’avoir des abstractions sans pénalité de performance. Mais aussi les possibilités de libération déterministe et implicite de n’importe quel type de ressource au travers du RAII, si on le compare au C — les autres langages à exceptions disposent d’un ramasse‐miettes (_garbage collector_) et suivent le _dispose pattern_ pour les autres ressources, voire offrent des facilités équivalentes au RAII (_try‐with‐resources_ en Java 7, `using` en C#, etc.). Certains estiment que si l’on ne profite pas de ces fonctionnalités, mieux vaut utiliser un autre langage.> **freem** : Mais justement : les ramasse‐miettes, ça pose des problèmes de performance que la RAII n’a pas. Par exemple, une connexion à une base de données en Java, si l’on ne la ferme pas manuellement, on risque de faire exploser la charge du [[SGBD]] tant qu’il ne passe pas pendant trop longtemps. On s’expose aussi à des ralentissements conséquents quand le ramasse‐miettes passe s’il a beaucoup de travail à faire. Choses qui auraient pu être faites à des moments moins critiques. Certaines ressources peuvent aussi se retrouver à manquer si l’on en alloue trop vite pour que le ramasse‐miettes nettoie tout... Bref, le ramasse‐miettes à ses avantages (pas besoin de s’occuper de gérer les choses) mais implique de maîtriser l’outil, sans quoi des problèmes différents arrivent. Les alternatives ================ * C ; * Rust ; * Go ; * Dart ; * Elixir ; * Phoenix ; * Pony ; * D ; * OCaml ; * Haskell ; * Ada ; * etc. Parmi ces langages, en excluant ceux qui ne sont qu’interprétés et ceux qui utilisent un ramasse‐miettes, les seuls qui pourraient remplacer C++ à performances égales sont donc : C, Rust et D. > **gbdivers** : Pourquoi exclure les interprétés et les ramasse‐miettes ? Ça serait l’occasion de tordre le cou a un mauvais argument sur le C++ : les performances. On entend souvent « Je veux faire des jeux vidéo, on m’a dit qu’il fallait les faire en C++ pour les perfs ». Alors qu’en pratique, les perfs ne sont pas critiques pour beaucoup d’applis et que les perfs dépendent aussi beaucoup de l’algorithme et des structures de données. Il vaut mieux n’importe quel langage correctement utilisé que le C++ mal utilisé.>> **wilk** : Peut‐être préciser ce que l’on entend par performance. Ce n’est pas la même chose au niveau d’une interface graphique, d’un serveur, d’un mobile, etc.>> **khivapia** : « Il vaut mieux n’importe quel langage correctement utilisé que le C++ mal utilisé » : argument valable pour tous les langages. Pour tirer le maximum de perfs de la machine physique, il ne faut pas de ramasse‐miettes (il n’y en a pas de temps réel et parallèle ; donc, s’il se réveille au milieu du jeu d’action, paf, grosse latence ! ) ni, bien sûr, de langage interprété (perte de perfs liées à la machine virtuelle).>> **gbdivers** : Je dis que « les performances du C++ ne sont pas forcement un critère pertinent », vous me répondiez « le C++ est plus performant ». OK, mais cela ne change rien à mon argument.>>> Pour détailler : écrire du code performant a un coût. Beaucoup n’ont pas le temps et les compétences pour mettre en place les méthodes permettant d’optimiser correctement le code. On peut écrire en C++ un code qui sera plus performant qu’un ramasse‐miettes, mais on peut également écrire pire. >> Un exemple concret : . Indéniablement, on peut avoir de meilleures performances si l’on sait tirer le meilleur de la machine. Mais combien sont capables de faire cela ? Et a‐t‐on le temps de faire cela sur un vrai projet ? C’est dans ce sens là que je dis que les performances ne sont pas toujours un bon critère.>> **Nicolas Boulay** : Pour prendre un exemple, je saurais faire ce genre d’optimisation, mais seul C/C++ me donne accès aux fonctionnalités pour faire le travail. Avec les autres langages, c’est simplement hors de portée, ou beaucoup plus complexe ([[assembleur]] ou appels de fonctions externes).>> **gbdivers** : Oui, mais tu reconnaîtras sans problème, je pense, que tous ceux qui veulent faire du C++ n’ont pas forcement ces compétences ou le temps de faire correctement les choses pour un projet donné (_deadlines_).>> L’argument « faire du C++ pour les performances » n’est donc pas valide seul. Il faut regarder le contexte du projet, les moyens disponibles, les compétences, etc. C’est un problème que l’on voit souvent sur les forums, où les débutants arrivent et disent « je veux faire un jeu, on m’a conseillé le C++ parce que c’est le plus performant ». Alors qu’en pratique, avec un autre langage, ça ne sera pas moins performant (parce qu’ils ne sont pas capables d’aller chercher les performances du C++).>> **freem** : Je suis d’accord avec _gbdivers_ (je n’arrête pas de lire _gdbivers_, grrr) : je ne compte plus les _push_back_ dans un `vector` dans une boucle de taille définie sans appel à réserver au préalable, et c’est bien moins complexe que le lien donné plus haut (pas encore lu, mais il est probable que je ne sache pas tout faire dedans). Je passerai également sur les _vectors_ multiples de même nombre d’éléments utilisés ensemble (_cache miss_ et appels à `malloc` inutiles). Et ce sont les bases pour écrire un code rapide en C++.>> **Nicolas Boulay** : Tous les moteurs de jeux ([Unity3D](https://unity3d.com/fr), etc.) sont écrits en C++, en revanche la logique des jeux elle‐même est en JavaScript, en C#, en Lua... En gros, cela demande moins d’efforts de faire propre avec n’importe quoi d’autre que C++, sauf si les performances sont une absolue nécessité. Critères pour adopter un nouveau langage ======================================== Mais pour qu’un langage soit adopté plusieurs ingrédients sont nécessaires : * un gage de pérennité ; * la facilité de débogage (déverminage) ; * la facilité d’augmenter un programme ; * une communauté, des meet‐ups, des entreprises ; * une galaxie de sites Web, d’outils divers, des bibliothèques, des tutoriels, de la documentation, etc. ; * des performances raisonnables. Sur ce terrain, le C++ est bien doté avec de très nombreux outils, bibliothèques et communautés. Popularité du C++ ================= Et toi, chère lectrice, cher lecteur, penses‐tu que les nouvelles fonctionnalités du C++ ne font que compliquer inutilement le langage le plus complexe que l’humanité ait inventé ? Ou, au contraire, es‐tu persuadé·e que, grâce à ces évolutions, le C++ va progresser dans le classement du _TIOBE Index_ et garder loin derrière des prétendants comme ~~[D](https://fr.wikipedia.org/wiki/D_(langage)),~~ [Dart](https://fr.wikipedia.org/wiki/Dart_(langage_informatique)), [Nim](https://fr.wikipedia.org/wiki/Nim_(langage)), [Rust](https://fr.wikipedia.org/wiki/Rust_(langage)) ou [Pony](http://www.ponylang.org/) ? ![Indice TIOBE de popularité des langages de programmation, la courbe en vert clair représente l’inexorable descente du C++, qui reste quand même en troisième place derrière Java et C, mais devant Python et C#](http://cdn.edureka.co/blog/wp-content/uploads/2016/06/TIOBE-index-2016.png) Autres sources : * ; * ; * ; * ; * . > **lmghs** : Autre indicateur : celui d’ohloh qui ne compte pas les recherches sur les moteurs de recherche, mais qui observe l’activité sur les projets FOSS → . Questions ========= * Faut‐il considérer le C++ comme langage potentiel au développement d’une nouvelle application ? * Est‐il pertinent d’apprendre le C++ aujourd’hui ? ## Go Go a été créé par Google parce que la compilation C++ devenait rédhibitoire pour ses applications serveur. C++ a été initialement créé comme une extension du C, gardant la compatibilité avec lui, et non un pur langage objet. Il y a donc un mélange entre objets et types primitifs (comme dans java). Toutefois, contrairement à Java ou C#, C++ ne fait pas de différences fondamentales entre les types primitifs et les objets définis par l’utilisateur : c’est au programmeur de décider si les types sont copiables, l’usage des références est explicite, l’allocation sur la pile est possible. Certaines briques « de base », telles que les conteneurs ou les `string`, sont toutefois arrivées tardivement dans la spécification, ou de manière incomplète. Ainsi, beaucoup de bibliothèques réinventent la roue, ce qui peut mener à des difficultés ou un surcoût (conversions multiples) dans l’interopérabilité entre bibliothèques. Le compilateur permet de faire de la vraie métaprogrammation. Et c’est réellement utilisé. La métaprogrammation en C++ repose sur un modèle de typage structurel. En revanche, il n’y a pratiquement aucune aide à la gestion d’erreurs, bien que C++11 ait introduit `static_assert`. Les messages d’erreurs liés à la métaprogrammation sont souvent particulièrement cryptiques et demandent un certain degré d’habitude ou d’expertise pour être compris, et un degré encore supérieur pour savoir écrire des bibliothèques fortement génériques provoquant des messages d’erreurs intelligibles. Voir à ce sujet la présentation de Roland Block donnée lors de la CppCon 2015 : [_Pruning Error Messages From Your C++ Template Code_](https://www.youtube.com/watch?v=2ISqFW9fRws) [[_diapos_](https://github.com/CppCon/CppCon2015/blob/master/Presentations/Pruning%20Error%20Messages%20From%20Your%20C%2B%2B%20Template%20Code/Pruning%20Error%20Messages%20From%20Your%20C%2B%2B%20Template%20Code%20-%20Roland%20Bock%20-%20CppCon%202015.pdf)]. À noter toutefois que les progrès récents du côté des compilateurs, tels que g++ ou Clang++, font que les messages d’erreurs sont un peu moins longs qu’avant. La gestion de la mémoire, et des ressources en général, en C++ est laissée à la charge du développeur. Cela a pendant longtemps abouti à la multiplication des problèmes tels que les fuites mémoires ou l’utilisation après libération, qui se manifestent aujourd’hui en autant de failles de sécurité. Toutefois, C++ fournit maintenant des outils intégrés à la bibliothèque standard, qui suivent le principe de RAII (_Ressource Acquisition Is Initialization_) pour gérer finement l’utilisation des ressources. Ce principe a été repris et généralisé dans le langage Rust (créé par Mozilla), qui veut se poser en alternative, mais reste aujourd’hui inférieur en termes d’écosystème ([[SIMD]], multicœur / OpenMP, _toolkit_ graphique façon Qt). D’un autre côté, si C++ trouve un moyen de détecter les vieilles constructions à éviter, que les types somme et le filtrage de type par motif (_type pattern matching_) est ajouté, que les pointeurs soient vus comme des types somme avec détection des « appels sur NULL » à la compilation, si des conteneurs sont introduits avec des interfaces _fold/map/reduce_ pour réduire les erreurs _off‐by‐one_ lors des parcours, si la compilation est plus rapide (peut‐être avec une gestion multi‐fichier du compilateur), si le modèle mémoire devient plus précis pour gérer les alignements mémoire, l’appartenance des objets en mémoire pour chaque fil d’exécution, et pour un éventuel ramasse‐miettes, C++ est là pour longtemps. ## Les spécifications _Tiré du commentaire : ._ La volonté de compatibilité des différentes versions de la norme pousse le comité de standardisation à ajouter des parties plutôt qu’à les remplacer. Cela pose plusieurs problèmes : - il est possible de voir différentes strates d’un mécanisme dans la spec et il n’existe pas vraiment d’outils pour savoir ce qui est bien ou pas ; - le maintien des programmes existant peut être compliqué pour se conformer à la nouvelle bonne pratique ; - les différentes bibliothèques utilisées ne sont pas forcément toutes homogènes et peuvent contraindre à utiliser toutes les parties de la spec. En outre, le comité de normalisation est fermé. Même si le langage est ouvert (son implémentation), les spécifications sont payantes et soumises à des droits. Une aberration qui entraîne parfois des soupçons de corruption ou du moins une volonté d’entretenir une complexité spéciale pour justifier des experts. Ces critiques sont toutefois largement injustifiées, les brouillons des spécifications (quasiment identiques à la version finale) étant en accès libre, et le comité de normalisation s’étant énormément ouvert à la communauté C++ depuis le début des années 2010. ## La bibliothèque standard pas homogène _Tiré du commentaire : ._ La bibliothèque standard est très poussée pour des domaines assez pointus (comme les mathématiques), mais ne propose rien pour des usages très courants comme le multitâche qui est, lui, géré nativement par Rust. > **lmghs** [Réponse au commentaire — brouillon à retravailler] : La situation a évolué depuis le C++11 qui a introduit les [[mutex]] et autre verrous, des fils d’exécution (_threads_) et des mécanismes pour l’asynchrone. L’asynchrone était assez imparfait et des efforts continus ont été investis dessus au point que les évolutions les plus visibles pour le C++17 concernent les aspects de la concurrence et du parallélisme. On observe une synergie importante avec la bibliothèque _open source_ HPX. > **Nicolas Boulay** : La méthode _lock/thread_ est une méthode qui engendre un paquet d’erreurs (situation de concurrence [_race condition_], inversion de priorité, [[interblocage]]...), un cerveau humain normal a trop de mal avec ces paradigmes. Ce qui semble marcher est le [passage de messages](https://fr.wikipedia.org/wiki/Passage_de_messages) (Erlang, Qt...), surtout si l’on sait faire du « zéro copie » (programmation linéaire). ## Des problèmes quotidiens semblent compliqués _Tiré du commentaire : ._ Des problèmes tout à fait quotidiens (comme le fait de n’inclure qu’une seule fois chaque fichier à la compilation) n’ont pas de solution triviale. Chaque développeur ou chaque projet gère donc cela à sa façon avec potentiellement des erreurs et sans capitalisation au sein de la communauté C++. La gestion de dépendance, la portabilité, le construction et la reproductibilité ================================================================================ *N. D. M. : Paragraphe à travailler, je jette des idées en vrac* La gestion de dépendances en C++ est complexe. il faut pouvoir compiler un logiciel dans un contexte reproductible, qui, dans le meilleur des cas : - est indépendant de la machine de compilation et de la machine cible ; - garantie que les bibliothèques utilisées seront toujours les mêmes (mêmes numéros de version, mêmes paramètres de compilation) ; - que le compilateur utilisé est le même ; - que tout cela est portable sur plusieurs systèmes ; - que le programme (binaire, répertoire de code source...) peut être livré facilement sur une machine cible non maîtrisée. En gros, qu’il vienne avec ses dépendances. Ce type de problème est souvent géré dans d’autre langages par des outils de gestion de dépendances, comme en Python avec [pip](https://en.wikipedia.org/wiki/Pip_(package_manager)), Haskell avec [Stack](https://en.wikipedia.org/wiki/Stack_(Haskell)), etc. Ces outils permettent de construire son programme dans un environnement restreint en fournissant tout ou partie des garanties listées au‐dessus. À ma connaissance, il n’existe pas d’outil multi‐plate‐forme de ce type en C++. Ainsi, chaque bibliothèque vient avec sa propre méthode de compilation et d’installation non générique et l’empaquetage d’un programme revient souvent à faire soi‐même, à la main, la construction de toutes les dépendances dans un répertoire dédié du projet, ce qui devient fastidieux quand on cherche aussi à s’assurer des numéros de version des outils de compilation utilisées.> **lmghs** : Il existe [_conan.io_](https://www.conan.io/) qui veut fédérer les choses, et Nuget sous Windows. À voir où cela va nous mener. Sous Windows, il n’y a rien de vraiment solide, chaque année voit son lot de gestionnaire de paquets, comme [Chocolatey](https://chocolatey.org/), mais rien de suffisamment robuste ou exhaustif permettant de choisir les versions exactes des bibliothèques et outils. Sous GNU/Linux, chaque distribution propose son gestionnaire de paquets, garantissant que les dépendances fonctionnent au sein du système. Cependant, si la version de bibliothèque fournie ne correspond pas à celle nécessaire au programme, on en revient à une gestion à la main. On peut aussi utiliser une technologie de conteneur léger comme [[Docker]] et installer une distribution GNU/Linux spécifique avec un ensemble de paquets choisis. Mais cette méthode pose le problème des performances. Or, une des raisons de l’utilisation de C++ étant les performances, on peut se retrouver bloqué [*citation/link needed*].> **freem** : Je ne comprends plus là... On parle de construire l’application (_build_, compilation, _linking_ ?) ou de l’exécuter ? Qu’il n’existe pas de gestionnaire de dépendances binaires est au contraire une bonne chose pour moi : c’est le boulot du système d’exploitation ça. Enfin, de la distribution autour du système d’exploitation.> Si le problème c’est de détecter les dépendances et de compiler en fonction, alors je trouve que _cmake_ est un très bon candidat, malgré un certain nombre de défauts (il semblerait que ce ne soit pas nickel niveau portabilité, bien que les textes que j’ai lu dans ce sens n’ont pas vraiment argumenté. Il y a aussi le fait que le système de construction ne puisse être promené d’un dossier à l’autre [usage de chemins absolus dans le _Makefile_ généré]). ## Avantages du C++ sur les autres langages Le C++ a malgré tout des avantages que l’on ne retrouve dans aucun autre ou pratiquement : * une manipulation bas niveau voire très bas niveau (du fait de sa proximité avec le langage C, aujourd’hui majoritairement utilisé dans les microcontrôleurs à la place de l’assembleur) qui permet des optimisations très fines (on peut même y intégrer de l’assembleur) ; il tient cette puissance de sa très forte proximité dans son fonctionnement avec l’ordinateur (au moins son [architecture de von Neumann](https://fr.wikipedia.org/wiki/Architecture_de_von_Neumann), les machines actuelles ont seulement l’apparence de machines de von Neumann, mais si l’on veut des performances, il faut aller plus loin) ; * il a évolué plus que tout autre langage en restant extrêmement rétro‐compatible, cela lui apporte : * un gage de stabilité (le code a de forte chance de compiler avec la prochaine version de C++) qui rassure les responsables des logiciels, * une capacité haut niveau tout en restant extrêmement performant qui n’a pas d’équivalent ; c’est ce que lui envient, entre autres, le Go ou le Rust ; * les systèmes d’exploitation étant écrits en C/C++, il est le langage pour parler nativement au système et donc efficacement, profitant de ces avantages, de nombreuses bibliothèques système ou bas niveau sont aussi en C ou C++ ; * l’écosystème C++ est riche de nombreuses bibliothèques inévitables dans certains domaines ; par exemple, de nombreux formats de fichiers et utilitaires de l’industrie du cinéma, tel que [[OpenEXR]], [[OpenFX]], [OpenColorIO](http://opencolorio.org), [OpenImageIO](http://openimageio.org), [Alembic](http://www.alembic.io/), [OpenVDB](http://www.openvdb.org/), [OpenEXRID](https://github.com/MercenariesEngineering/openexrid), etc., sont fournis sous la forme de bibliothèques C++. Bien que des bibliothèques de liaison (_[[bindings]]_) vers d’autres langages existent, celles‐ci ne garantissent pas forcement une compatibilité parfaite, aussi bien en termes de fonctionnalités que de performances. Par exemple, un tableau dynamique C++ (`std::vector`) n’est pas forcement compatible au niveau binaire avec son équivalent Rust, imposant la création d’une interface. Note aux lecteurs‐commentateurs =============================== Je donne des exemples de bibliothèques de cette industrie parce que c’est celle que je connais et c’est actuellement la seule raison qui justifie notre utilisation du C++. D’autre personnes sont invitées à ajouter des exemples venant d’autres industries. De façon amusante, on dit souvent que des langages comme Python servent à faire la glu sur les entrées d’un programme et que le cœur est écrit dans un langage plus « sérieux » type C++. Nous, nous aurions plutôt tendance à vouloir utiliser du Rust ou du Haskell pour le cœur et à utiliser C++ pour la glu et les entrées‐sorties. C’est un point de vue assez amusant et à contre‐courant des idées habituelles. Et c’est ce point sur les performances tout en étant relativement haut niveau qui fait du C++ un langage que l’on ne peut remplacer dans beaucoup de projets. Les seuls candidats sont des langages aussi très anciens et qui finalement ont moins évolué (Pascal, Fortran...). Bien souvent, on développe une application dans un langage de haut niveau (Python, PHP, Ruby ou autres). Puis vient un moment où, pour des raisons de performance, on doit réécrire les parties les plus sollicitées. Par exemple, dans notre société, on développe en PHP et [awk](https://fr.wikipedia.org/wiki/Awk). Et quand les charges serveurs augmentent, on réimplémente le cœur en C/C++ et on garde une couche Bash ou PHP au‐dessus. La raison pour laquelle on ne développe pas directement en C/C++, est que son développement et sa maintenance sont plus longs. En C/C++, un bogue peut survenir sur un détail difficile à identifier, là où le PHP, très tolérant, accepte les problèmes. Il y a aussi en C/C++ le risque de ne plus savoir quelle version exacte du code avait servi à compiler la précédente version et donc une difficulté à savoir si lors d’une mise à jour les dernières évolutions du code ne vont pas perturber le fonctionnement en place. Autrement dit, il est difficile de savoir entre deux exécutables quelles sont les modifications apportées. Le C++ fait aussi son apparition dans l’embarqué. Là où il n’y avait qu’assembleur et C, la suite logique pour des composants toujours plus complexes est le C++. D’abord à travers l’utilisation simple de classes, permettant une meilleure architecture soit par encapsulation, soit par héritage. Mais de plus en plus, on voit arriver des morceaux de la bibliothèque standard, au fur et à mesure que l’embarqué accepte l’allocation dynamique de mémoire. Une autre force du C++ est la base d’utilisateurs le connaissant, du fait de sa longévité — il date de 1983.

AltStyle によって変換されたページ (->オリジナル) /