Cox contre Stroustrup

À l'occasion de la rentrée, j'ai relu le code de mes divers frameworks, et j'ai constaté à quel point ils étaient mal programmés, donc je me suis dit que c'était peut-être le bon moment pour les réécrire. Habituellement je programme en C++ (conçu par Bjarne Stroustrup), mais j'ai découvert récemment un nouveau langage, l'Objective-C (conçu par Brad Cox et Tom Love), qui est aussi séduisant. Je vais décrire et argumenter ici mon choix final de langage.

Le tenant du titre

Je programme plus ou moins en C++ depuis des années. Je ne me souviens plus trop quand est-ce que commencé à utiliser sérieusement des classes, mais ça doit bien faire une demi-décennie.

J'aime bien le paradigme orienté objet, et il me semble adapté aux problèmes auxquels je suis confronté. En fait ce qui m'a surtout séduite, c'est le concept de la structure de donnée livrée avec le code pour agir dessus. Les choses comme l'héritage et tout ça c'est secondaire pour moi. L'autre chose que j'aime beaucoup dans le C++, c'est que l'on est pas enfermé dans un paradigme, et on peut prendre un raccourci évident (ou pas) si besoin est.

Ce que je trouve un peu frustrant dans le C++, c'est qu'il est bourré de trucs et de machins que je n'utilise pas. De par mon éducation informatique dans la demoscene, quand je programme je pense encore en assembleur. Du coup quand je programme en C++, je pense en assembleur et je convertis cette pensée en C++ (avant que le compilateur ne la reconvertisse en assembleur puis en langage machine). Donc je n'arrive pas du tout à utiliser les boîtes noires que je ne comprends pas ou que je n'arrive pas à faire rentrer dans mon schéma de pensée.

Un exemple de telle boîte noire est la STL, qui est en quelque sorte la librairie standard C++. Il se passe plein de choses étranges avec, comme des opérateurs = qui font de la copie ou pas, de la mémoire qu'on sait pas trop si elle vient de la pile ou du tas, et des allocations qu'on sait pas troip ni où ni quand elles sont faites, avec toujours le doute sur est-ce que ce sera libéré un jour ou pas. Du coup, je fais une allergie à la STL, et on pourrait dire que du coup je ne fais pas vraiment de C++, mais plutôt du « C avec des classes ».

L'héritage multiple est un truc moins mystérieux, mais dont je n'ai jamais vraiment ressenti le besoin, et que je n'ai donc jamais utilisé. Pareil pour les templates, je vois vaguement le fonctionnement, mais ce n'est pas vraiment satisfaisant, du coup je ne m'en suis servie qu'uen fois, et encore, j'ai dû me forcer, lorsque j'ai implémenté un arbre AVL générique.

Il y a probablement pas mal de fonctionnalités vraiment puissantes et de concepts avancés de C++ qui me passent largement au dessus, et dont j'ignore probablement même l'existence. C'est ça, la vie d'autodidacte, mais ce que je fais marche, et tant que ça marche et que personne d'autre que moi ne regarde mon code, je peux faire ce que je veux.

Le challenger

Il y a quelques mois, j'ai entendu parler de l'Objective-C, qui est comme le C++ une extension orientée objet du C. Et comme c'est une extension assez légère, l'apprentissage n'est pas un problème.

Je dois avouer qu'initialement j'ai été surtout attirée par le côté esotérique de l'Objective-C, le p'tit langage obscur comme ça que personne ne connaît. D'un autre côté, on pourrait aussi dire que si personne ne s'en sert, ce n'est peut-être pas pour rien. Et effectivement, ce n'est pas pour rien, mais c'est pour une raison commerciale, AT&T c'est quelque chose de beaucoup plus lourd qu'une start-up de dix personnes. Et ça, ça fait encore un point a priori de plus pour ce langage.

De prime abord, toute l'extension orientée objet est une immense boîte noire, avec une syntaxe d'appel de fonction étrange (non-C, en fait), mais avec des fonctionnalités très intéressantes. Mais vu mon allergie aux boîtes noires, je ne pouvais décemment pas laisser ça comme ça. Alors au lieu d'abonner le langage sans autre forme de procès, j'ai quand même péniblement cherché une description du fonctionnement de ses entrailles. Et j'ai trouvé, c'est plutôt ingénieux, avec de bons caches, mais relativement gourmand en mémoire. On a rien sans rien.

Ce que j'aime a posteriori dans l'Objective-C, c'est son minimalisme : il n'y pas trente-six mille fonctionnalités ajoutés, il y a juste l'orienté objet. Seulement une syntaxe pour appeler une méthode (« envoyer un message ») et une syntaxe pour déclarer les classes. Pas de STL, pas de template, pas d'héritage multiple, pas de trucs bizarres que je ne connais même pas.

Et ce fameux système d'appel, qui permet de faire plein de choses fantastiques : ajouter des méthodes en runtime à des classes dont on ne connaît rien, prendre la place de certaines classes, déléguer des tâches à d'autres classes, etc. Bref, en caricaturant, l'Objective-C ne fait qu'une seule chose de plus que le C, mais il la fait bien.

Le duel

Une autre force du C++, c'est que l'on ne paye que pour ce que l'on utilise. Il n'y a aucun overhead associé à une fonctionnalité que l'on utilise pas. Si je n'utilise pas d'héritage ni de fonction virtuelle ni de template ni quoi que ce soit, que je fais juste la structure de donnée livrée avec le code pour la modifier dans le sens le plus minimaliste, j'ai exactement le même résultat que si je l'avais fait en C avec des structures et des fonctions dédiées. Et un accesseur inline permet de garder toute la propreté des choses publiques ou privées, sans aucun impact sur les performances.

À l'inverse, l'Objective-C nous impose tous les coûts en mémoire et en temps liés à la puissance phénoménale de son système d'appel. Du coup l'accesseur de base devient très lourd. Alors les fan d'Objective-C disent avec raison que sur les machines actuelles, ce n'est pas un coût si terrible. En général, c'est vrai, mais pour la boucle interne d'une simulation Monte-Carlo qui tourne pendant une semaine, c'est quand même un overhead dont on se passerait bien.

Ce n'est pas si terrible en fait. Par exemple les parties qui doivent être bien optimisées je peux les faire en C ou même en C++. Par exemple mon code pour les vecteurs, il n'y a pas d'héritages ou de fonctions virtuelles ou de trucs bizarres, je pourrais en faire une structure C avec des fonctions globales pour les manipuler. Je trouve que c'est une regression par rapport aux classes C++, mais c'est un sacrifice que je serais prête à faire si la contrepartie en vaut le coup.

Et parmi la multitude de fonctionnalités du C++, je me passe très bien de la plupart : l'héritage multiple je n'en ai pas encore ressenti le besoin, les templates sont inutiles avec le système de l'Objective-C, la surchage des opérateurs j'ai un avis mitigé dessus (c'est pas mal mais c'est trop facile de faire des trucs immondes avec), les multiples const ça impose de la rigueur mais je peux survivre sans, et tous les autres jouets que je ne connais pas j'ai très bien vécu sans jusqu'ici.

Le résultat

Il y a cependant trois petites fonctionnalités du C++ qui me manquent cruellement au point d'être rédhibitoires pour l'Objective-C.

D'abord, la surcharge des fonctions. Au lieu d'utiliser juste son nom pour identifier une fonction, utiliser toute sa signature. C'est souvent fait en C en ajoutant à la main un suffixe déterminé en fonction de la signature. Bref, autant laisser au compilateur le soin de faire ça, et éviter des noms de fonctions à rallonge ou hyperabrégés.

Ensuite l'absence de constructeur et de destructeur automatiques. J'aime que mon code soit blindé, et que si j'oublie quelque chose je le remarque rapidement. Une variable non initialisée, c'est typiquement le genre de choses qui peut être très difficile à trouver, et qui peut mettre un sacré bout de temps avant de se révéler. Et c'est toujours mieux que ce soit le compilateur qui garantisse que le machin est initialisée plutôt que les « bonnes habitudes » du codeur. Le codeur n'est fondamentalement pas digne de confiance, car il est humain.

Enfin l'impossibilité d'instancier des classes sur la pile. Surtout pour les classes basiques, si on peut économiser une allocation, c'est toujousr ça de pris. Au début je n'aimais pas cette fonctionnalité du C++ et je l'évitais, en faisant tout à coups de new et delete à la place ; jusqu'à ce que je comprenne son fonctionnement, et où la mémoire est allouée et libérée. Depuis je l'utilise massivement, pour avoir la garantie que tout est libéré en sortant du scope. Encore une fois parce que le codeur n'est pas digne de confiance.

Conclusion

C'est vraiment pour de petites choses que l'Objective-C n'a pas été retenu pour mon choix. Je vais continuer mon C++ exotique qui ignore superbement un bon paquet de fonctionnalités du langage. Je crois même que ce ne sont que des modifications mineures à faire pour arriver à un Objective-C qui me plaise.

Je pense qu'au final, ces petites sont le reflet d'une différence philosophique plus profonde. L'Objective-C me semble être un mélange entre C et un truc orienté objet, mais avec une frontière bien définie entre les deux. Pas un mélange comme un mélange de couleurs, mais plutôt comme un mélange de petits cailloux et de gros cailloux. On fait alternativement du C et de l'orienté objet, mais jamais les deux en même temps. À l'inverse, le C++ est comme un gigantesque shaker où tous les éléments sont assemblés pour faire un tout homogène.

Du coup, l'Objective-C est un peu coincé dans son paradigme orienté objet ou son paradigme structuré, alors que le C++ me semble pouvoir faire du multiparadigme fluide, et même du grand n'importe quoi si c'est ça qu'on veut faire.

Voilà, je reste un mouton dans le courant principal avec le C++, et je ne vais pas explorer plus avant le monde merveilleux et méconnu de l'Objective-C. C'est la vie.

Publié le lundi 3 septembre 2007 à 16:59.

Catégories : Boulot Geek

Commentaires

1. Le lundi 3 septembre 2007 à 18:15, par _FrnchFrgg_ :

Il me semble que la STL c'est vraiment des trucs super simples, hein, genre le successeur de strcpy et consorts... En plus tu as toutes les sources dispo, dans le cas de libstdc++.

Sinon, moi je suis vraiment un grand fan du C++ justement pour ses boites noires, enfin pour sa capacité à créer des boites noires. Parce que bon, j'aime pas trop utiliser les boites noires des autres. Mais bon, quand j'utilise gtkmm (le binding C++ de gtk), je suis bien content d'avoir une boite noire "fenêtre", et je me fous des données qu'il y a dedans, de comment sont représentées les différentes propriétés X, etc... Tout ce que je veux c'est avoir moyen d'appeler des fonctions dessus.

En fait, pour moi, les classes, c'est ni plus ni moins que des namespaces de fonctions avec détermination automatique en fonction du premier argument (qu'on passe avant le nom de la fonction par sucre syntaxique).

Objective C c'est en fait le seul binding des API Cocoa de MacOs X, et c'est donc plus utilisé qu'on ne croit, au moins dans la couche graphique des applis (j'ai lu ça dans le rapport de thèse d'un gars dont la thèse était "écriture de gtk en natif cocoa au lieu d'un passage par un serveur X")

Ce qui me manque en C++ c'est la considération de fonctions comme citoyens de première classe, avec tout ce que ça peut amener comme concept d'applications partielles, de curryfication, etc... Vive les ML (d'ailleurs quand je programme en fonctionnel je n'utilise jamais de paradigme objet, c'est grave docteur ?)

2. Le vendredi 7 septembre 2007 à 13:46, par Natacha :

On est d'accord sur le fait que le C++ est un truc formidable pour l'abstraction, et avoir des choses qui peuvent être considérées comme des boîtes noires les unes pour les autres. J'ai plusieurs fois changé complètement l'architecture interne d'une classe sans avoir à toucher à ce qui est autour. Je suppose donc que ça s'étend aussi bien aux interfaces graphiques, même si je n'en ai encore jamais ressenti le besoin : l'interface la plus évoluée que j'ai faite, c'était avec imlib, pour afficher un film enregistré dans le format bizarre du labo', et mettre le résultat de mes analyses en surimpression. Autrement je communique par ligne de commande ou par sockets, ça ne demande pas vraiment la puissance d'abstraction du C++.

Bon par contre là où on n'est pas d'accord du tout, c'est sur les ML et la programmation fonctionnelle, parce que là il y a au moins quarante-douze niveaux de boîtes noires entre le code et le CPU. Je suis d'accord que c'est bien pour les trucs théoriques, mais quand je code une vraie application pour un vrai CPU, je sais penser qu'en bas niveau, et je suis totalement hermétique à tous les gargarismes de curryfication, de garbage collector, et autres UML. Pour moi il n'y a qu'une classe de citoyens : les tas de bits. Les fonctions c'est juste des pointeurs vers un bout de code, au même titre qu'un tableau C c'est juste un pointeur vers un bout de données. Curryfie-moi ça...

3. Le samedi 8 septembre 2007 à 12:44, par _FrnchFrgg_ :

Je pense que tu ne connais pas assez OCaml, alors. Parce que bon, niveau efficacité, c'est pas loin du C, quand c'est compilé (avec les optimisations CPU). Et pour ce qui est des ramasse-miettes (traduction FTW), non seulement il commence à y en avoir des vraiment efficaces, mais surtout il en existe même pour le langage C (notemment, il y en a un dans Mozilla). Et à partir d'une certaine taille, avec des références circulaires, un GC est quasiment la seule manière de s'en sortir. En tout cas, je trouve ça plus propre que les bêtes AddRef/Release.

Après, ça ne se prête pas à tout, mais écrire un parser/lexer/analyseur syntaxique en OCaml, c'est une joie, en fait tout ce qui mange ou crache des structures un peu complexes, sur lesquelles tu veux "matcher"

C'est sûr que tu peux faire la même chose en C++, notemment avec l'héritage et les classes de base, qui peuvent imiter les types composés en Caml.

Ce qui fait la puissance d'un langage fonctionnel, c'est justement la possibilité de passer des fonctions partielles, finalement ce ne sont que des "callbacks" avec des clôtures, c'est pas si difficile que ça à implémenter. Après, le GC, c'est ni plus ni moins qu'une petite aide (et parce que ça ferait très moche de devoir instancier au beau milieu d'une écriture fonctionnelle), la syntaxe c'est du sucre, le pattern matching pourrait très bien être une extension C++, etc...

Enfin, je m'en doutais que les ML tu n'aimais pas. Nini non plus, d'ailleurs. Elle préfère le C. Au moins le C, c'est des ordres à la queue-leu-leu (elle a commencé à apprendre, ça l'amusait bien), alors que le fonctionnel c'est des maths, et ça ça lui sort par les yeux.

Ah, les femmes ! ;) gne

4. Le vendredi 14 septembre 2007 à 17:47, par Natacha :

Mon problème avec les langages fonctionnels et avec les garbage collectors n'a rien à voir avec l'efficacité. J'ai l'impression de me retrouver à l'époque où je disais haut et fort que l'assemble est de loin mon langage préféré (ce qui est encore le cas, d'ailleurs). Je me fous copieusement de l'efficacité d'OCaml, à un point que tu n'imagines probablement pas. Mon problème, c'est une incompatibilité majeure dans la façon de penser.

J'ai été élevée à la demoscene. C'est moche, mais c'est la vie, on n'y peut rien. Je veux bien m'amuser à convertir en un langage quelconque ma pensée en assembleur, pour qu'un compilateur puisse ensuite le reconvertir en assembleur, mais il y a des limites. Le C de base est assez proche de la machine pour que je m'en sorte. Le Caml, éventuellement O- si un jour je l'apprenais, je pourrais y toucher si je fais des math' ou de l'informatique théorique, mais pas pour programmer en vrai un vrai truc qu'un CPU décodera. C'est juste pas pour moi, c'est tout.

Petit retour sur le sujet initial de l'article : on m'a prédit la mort prochaine de ma façon de faire du C++ (avec gcc 4.3), du coup je vais peut-être me résigner à l'Objective-C ou au C pur. On verra quand le tumulte intérieur provoqué par cette annonce sera calmé, ça fera probablement un article de blog.

5. Le lundi 8 octobre 2007 à 1:56, par ralphy :

J'ai longtemps programmé en C uniquement. Il faut dire qu'à l'époque où je commençais à programmer à titre professionnel, en tant que développeur de jeux vidéo, on commençait à peine à abandonner l'assembleur du profix d'un langage structuré tel que le C. Cependant, à force, je m'étais rendu compte que je programmais d'une manière se rapprochant de plus en plus du C++ et de la programmation objet, le tout avec un langage finalement peu adapté qu'est le C. Aussi, je suis passé au C++ comme suite logique.

Je ne connais pas le Objective-C, mais celui-ci semble intéresser un grand nombre de développeurs que le C ne satisfait plus et qui ne souhaitent pas pour autant se plonger dans les subtilités du C++.

En termes de performances, j'aurais tendance à dire que le C++ est particulièrement efficace, malgré une syntaxe qui peut en rebuter plus d'un. La généricité et le polymorphisme, notamment, sont des concepts qui peuvent faire peur au niveau des performances, mais le C++ permet justement de les rendre particulièrement efficaces d'un point de vue des performances tout en gardant un code compact, à défaut d'être clair.

Mais il faut relativiser l'usage du C++ de nos jours pour une question de performances. En effet, de plus en plus, les langages compilés JIT prennent le dessus, car ils permettent ce que le C++ ne permet pas, du moins, ne le permet pas sous sa forme actuelle : du code compilé dynamiquement. Ainsi, en théorie, un langage faisant appel à une machine virtuelle compilant le code générique en code machine au vol a cet avantage de pouvoir adapter le code produit au processeur, d'une part, mais aussi aux données, même si cela ne semble pas se faire encore actuellement. D'un autre côté, les compilateurs C++ permettent de générer du code spécifique à chaque famille de processeurs, et les outils de profilage (VTune rules!) permettent d'identifier les goulots d'étranglement en vue d'une optimisation du code de manière spécifique aux données. Le C++ est donc le langage de choix pour tous ceux qui cherchent un code maintenable doté de performances excellentes. Ce n'est donc pas un hasard si c'est le langage le plus utilisé dans le développement de jeux vidéo.

Cela étant, ceci pourrait fort bien changer à l'avenir. Le C#, avec son framework .Net, permet un développement d'applications particulièrement rapide, et le code fourni est tout à fait convainquant, autant par sa taille que sa vitesse d'exécution. Les technologies et la machine virtuelle .Net est déjà supportée par Windows, Mac OS X (si je ne m'abuse) et Linux, ce qui permet de faire des applications multi-plateformes à moindres frais. Avec par ailleurs l'arrivée de Microsoft Silverlight, ces applications pourront être intégrées aux pages web, ce qui les rendra d'autant plus intéressantes au niveau de la facilité de déploiement.

6. Le vendredi 12 octobre 2007 à 14:34, par Natacha :

C'est vrai que l'Objective-C est une surcouche du C beaucoup plus fine que le C++, du coup ça permet de s'y mettre plus facilement et de ne pas passer des années à apprendre à maîtriser tous les concepts. C'est d'ailleurs ce qui me séduit dans l'Objective-C, c'est plus satisfaisant que le petit sous-ensemble du C++ auquel je me restreint.

Le C++ est extrêmement efficace, dans certains cas plus que C (il me semble que l'inline du C++ est plus puissant que celui du C99, ou alors c'est seulement qu'on parlait de C89, à vérifier). Il est même nettement plus efficace que l'Objective-C, donc je comprends que même si les deux langages étaient aussi répendus le jeu vidéo se troune plutôt vers le C++. La grande force de l'Objective-C, c'est sa flexibilité, ce qui permet de faire des choses très élégantes au niveau des interfaces graphiques.

Mais plus généralement, la différence fondamentale entre le C++ et l'Objective-C est à un niveau philosophique, on peut juste dire que l'un est mieux que l'autre pour telle chose précise, mais à l'échelle d'une application c'est juste un choix différent de compromis. C'est dommage que l'Objective-C reste obscur pour les raisons commerciales de son lancement.

Pour le reste, on arrive à la différence majeure entre nos deux points de vue. J'ai déjà pu constater dans d'autres discussions que tu as un point de vue très « professionnel », limite « commercial ». Ce n'est pas une critique, c'est juste un point de vue que je ne partage pas, ce qui est compréhensible vu que je suis étudiante, en train de « travailler » dans une structure académique, et en programmant principalement comme loisir. Du coup, je parle philosophie et choses à mon goût ou pas, alors tu parles de vitesse de développement et de facilité de déploiement.

Le JIT, en ce qui me concerne, c'est défintiviement non. J'ai été élevée par la demoscene, à l'assembleur, et j'imagine que certains pourraient dire que c'est pire qu'avoir été élevée au BASIC. J'arrive à faire du C ou du C++, mais j'ai beaucoup de mal avec les langages de plus haut niveau. Et comme je n'ai aucune contrainte financière sur ma programmation, si j'ai décidé que le java (ou n'importe quel autre langage) n'est pas à mon goût, même pour de mauvaises raison, je peux l'ignorer complètement.

Et quant aux productions de Micro$oft que tu cites, C#, .Net, Silverlight et tout, c'est peut-être super-bien dans un environnement professionnel, mais philosophiquement et pratiquement ce sont des choses que j'exècre. En choisissant le TLD de ce site, j'ai pris .org et non .net justement pour cette raison. Pourquoi tant de haine ? Parce qu'ils sont là pour se faire des sous, quitte à pigeonner le client, ou à le bloquer dans leurs systèmes. .Net sous linux, c'est une vaste blague, et si ça marche à peu près vaguement, ce n'est pas grâce à des commerciaux.

Je comprends qu'on puisse n'avoir aucune réticence à devenir prisonnier d'une technologie propriétaire dont on ne sait rien, mais moi je ne supporte pas ça. J'ai trop longtemps parlé directement avec le CPU pour laisser des bidules et des machins sous secret commercial se mettre entre lui et moi.

7. Le vendredi 19 octobre 2007 à 21:49, par Une araignée au plafond :

Les intérêts de Microsoft à supporter l'open source sont relativement nouveaux, mais tout à fait réels : Microsoft a toujours cherché à s'imposer comme leader du développement de logiciel. Il fournit ainsi le système d'exploitation (Windows), les logiciels (Office), les technologies (.Net, Silverlight), ainsi que les outils de développement (Visual Studio). Mais pour ne pas se limiter à Windows, les technologies Microsoft, d'une part, ainsi que les logiciels Microsoft, d'autre part, s'ouvrent aux nouveaux environnements, dont les environnements open source comme Linux, mais aussi Mac OS X, basé sur du code open source, mais devenu propriétaire.

Mais Microsoft (qui vient de se faire approuver deux licences open source par l'OSI) ou Apple ne sont pas pour autant les seuls acteurs du développement informatique : Sun, qui a lancé le Java dans les années 1990, vient de rendre son langage open source, permettant à des tiers de s'y pencher. Du coup, l'argument "anti-commercial" ne peut plus tenir pour t'empêcher de regarder de plus près les langages compilés JIT et autres solutions à base de machines virtuelles.

Car je maintiens : si les langages JIT tels que le Java ou la famille de langages .Net peuvent aujourd'hui souffrir de quelques performances insuffisantes en comparaison de compilations natives, ce sont, à terme, des solutions beaucoup plus intéressantes, d'un point de vue théorique, du moins. Car contrairement à la compilation statique, les programmes à base de machine virtuelle et de JIT peuvent éventuellement décider de s'optimiser en temps réel en fonction des données qu'ils traitent, et non de manière générique comme c'est le cas des programmes habituellement compilés avec du code statique. En tant que développeur issu de la scène démo, l'argument de la performance ultime ne devrait pas t'être étranger, de sorte à ne pas totalement rejeter une si jolie voie à explorer...

Copyright © 2007-2008 Natacha Kerensikova

Lithium Blog - commit dad867adc7a3fc6476990c37fcfa09685831b7d9 - Thursday 7 February 2008