tag:linuxfr.org,2005:/users/g-bleuLinuxFr.org : les contenus de G.bleu2020-02-13T10:21:07+01:00/favicon.pngtag:linuxfr.org,2005:Diary/389342020-02-04T19:26:56+01:002020-02-06T10:18:32+01:00Cette année on a pas mal parlé d’Epic à la GodotConLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<p>Ces deux derniers jours, et dans la plus pure tradition du « <a href="//linuxfr.org/users/serge_ss_paille/journaux/de-retour-du-fosdem-2020">il en reste un peu je vous le met aussi ?</a> », se tenait hier et aujourd’hui à Bruxelles <a href="https://godotengine.org/article/schedule-godotcon-2020-brussels">la GodotCon</a>.</p>
<p>À côté de la grande fête au village en plein air qu’est le FOSDEM, la conférence annuel autour du <a href="https://godotengine.org">moteur de jeu Godot</a> fait plus figure de réunion de famille dans le calme feutré des locaux de la <a href="http://ludus-academie.fr/">Ludus Académie</a>.</p>
<p>Cette année, c’est donc une centaine de personnes (oui, ça commence à faire une sacrée famille !) qui étaient réunies pour des conférences, des démonstrations de jeux, des discussions sur la <em>roadmap</em> du moteur et, bien sûr, du café (parce qu’on est des développeurs) et des gaufres (parce qu’on est en Belgique) !</p>
<p>L’événement ayant était diffusé en direct, tout est disponible sur la <a href="https://www.youtube.com/c/GodotEngineOfficial">chaîne YouTube du moteur</a>.</p>
<p>Et cette année, c’est une sacrée nouvelle qui a été annoncée : <a href="https://godotengine.org/article/godot-engine-was-awarded-epic-megagrant">Godot a été retenu pour un Epic MegaGrant de 250 000 dollars !</a></p>
<p>Ce financement permettra de soutenir le développement des fonctionnalités clé de la version 4.0 du moteur (<a href="https://godotengine.org/article/here-comes-godot-3-2">la 3.2 vient de sortir</a>) : le passage à l’API graphique <a href="https://fr.wikipedia.org/wiki/Vulkan_(API)">Vulkan</a> pour le moteur de rendu, ainsi que la refonte de GDScript (le langage de script du moteur) afin d’en améliorer les performances.</p>
<div><a href="https://linuxfr.org/users/g-bleu/journaux/cette-annee-on-a-pas-mal-parle-d-epic-a-la-godotcon.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/119339/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/g-bleu/journaux/cette-annee-on-a-pas-mal-parle-d-epic-a-la-godotcon#comments">ouvrir dans le navigateur</a>
</p>
G.bleuhttps://linuxfr.org/nodes/119339/comments.atomtag:linuxfr.org,2005:News/329632012-05-16T15:20:33+02:002012-05-17T09:26:30+02:00Retour d'expérience sur GoLicence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<div><p>Je viens de finir un petit projet en Go la semaine dernière, un assembleur vers du MIPS simplifié. Voici un petit retour d'expérience, en espérant que ça serve !</p>
<p><abbr title="Note des modérateurs">NdM</abbr> : merci à G.bleu pour son journal.</p></div><ul><li>lien nᵒ 1 : <a title="http://linuxfr.org/users/gbleu/journaux/retour-d-experience-sur-go" hreflang="fr" href="https://linuxfr.org/redirect/82041">Journal à l'origine de la dépêche</a></li><li>lien nᵒ 2 : <a title="http://sourceforge.net/projects/gopiler/" hreflang="fr" href="https://linuxfr.org/redirect/82045">gopiler</a></li></ul><div><h2 id="sommaire">Sommaire</h2>
<ul><li>
<a href="#toc_0">Mise en situation</a>
</li>
<li>
<a href="#toc_1">Le programme</a>
<ul><li>
<a href="#toc_2">Rentrons dans le code</a>
</li>
<li>
<a href="#toc_3">Et Go dans tout ça ?</a>
<ul><li>
<a href="#toc_4">Le makefile</a>
</li>
<li>
<a href="#toc_5">Les tests</a>
</li>
<li>
<a href="#toc_6">Le multiplatforme !</a>
</li>
<li>
<a href="#toc_7">Les outils en plus</a>
</li>
</ul></li>
<li>
<a href="#toc_8">Écrivons un peu de code</a>
<ul><li>
<a href="#toc_9">Le retour de plusieurs variables</a>
</li>
<li>
<a href="#toc_10">La gestion des options</a>
</li>
<li>
<a href="#toc_11">Les conteneurs</a>
<ul><li>
<a href="#toc_12">Map</a>
</li>
<li>
<a href="#toc_13">Array</a>
</li>
<li>
<a href="#toc_14">Slice</a>
</li>
</ul></li>
<li>
<a href="#toc_15">L'héritage</a>
<ul><li>
<a href="#toc_16">Les structures</a>
</li>
<li>
<a href="#toc_17">Les méthodes</a>
</li>
<li>
<a href="#toc_18">Les interfaces</a>
</li>
</ul></li>
<li>
<a href="#toc_19">Les autres fonctionnalités</a>
<ul><li>
<a href="#toc_20">Les goroutines</a>
</li>
</ul></li>
</ul></li>
</ul></li>
<li>
<a href="#toc_21">Conclusion</a>
</li>
</ul><h2 id="toc_0">Mise en situation</h2>
<p>Pour mes études, j'ai un projet (le dernier avant la vie active !) de réalisation de microprocesseur MIPS « <em>from scratch</em> ».</p>
<p>Le CPU est designé sous Xilinx ISE (grosso modo un IDE dans lequel on peut réaliser des designs de composants à base de portes logiques). Par la suite, il sera chargé sur un FPGA, celui-ci connecté à un robot afin de lui faire suivre une ligne sur le sol.</p>
<p>De fait, une fois le CPU designé, il faut réaliser un programme en assembleur MIPS, puis le convertir en binaire afin de l'intégrer dans le design du CPU sous la forme d'un module en VHDL. En gros, en entrée du module arrive l'adresse du <em>program counter</em> et le module sort l'instruction correspondante.</p>
<p>Les plus attentifs auront déjà pointé du doigt le souci : comment convertir proprement le code assembleur MIPS en binaire ?</p>
<ul><li><p>À la main, ne riez pas, c'est ce que m'a proposé mon prof quand je lui ai posé la question ! À sa décharge, les élèves suivant ce cours ne sont pas informaticiens mais plutôt orientés électronique.</p></li>
<li><p>Utiliser un assembleur déjà existant. La solution « ne pas réinventer la roue » de référence. Le problème : l'<em>output</em> sera en binaire (logique, me direz-vous) mais je veux du code binaire lisible ! (en gros mon output doit être 000101011100110 afin de pouvoir directement copier coller le code dans le fichier de mon module VHDL). Ajouter à cela que je ne veux pas de <em>header</em> ELF ou quoi que ce soit, juste la transcription du code que j'ai écrit. Je pense, bien-sûr, qu'il y a des solutions pour arriver à ce que je veux. Néanmoins, j'ai du temps en ce moment et apprendre un nouveau langage me semble plus formateur qu'apprendre les options d'un outil qui ne me servira plus par la suite !</p></li>
<li><p>Écrire un assembleur à la main. La solution que j'ai choisie, et qui me donne donc la possibilité de réaliser ce journal ! Un assembleur n'est pas aussi complexe qu'un compilateur (et de loin !), mais permet déjà de s'amuser sur un nouveau langage.</p></li>
</ul><h2 id="toc_1">Le programme</h2>
<p>Linus Torvald :</p>
<blockquote>
<p>Show me the code !</p>
</blockquote>
<p><a href="http://sourceforge.net/projects/gopiler">Céans mon bon monsieur !</a> En espérant que tout le monde aime SourceForge…</p>
<p>Comme dit plus haut, il s'agit d'un assembleur MIPS « simplifié » :</p>
<ul><li><p>Toute les instructions ne sont pas disponibles (pas de jump ou de subi par exemple). La raison est tout simplement que mon processeur ne prend pas en charge ces instructions et que je préfère avoir une erreur à la compilation si j'oublie ce détail que de devoir débugger une erreur qui n'en est pas une par la suite… Malgré tout, il est très simple d'ajouter ces fonctionnalités comme nous allons le voir.</p></li>
<li><p>Pas de header ELF pour le programme. Comme j'ai dit, l'idée est de copier la sortie de l'assembleur dans un fichier pour que mon cpu l'utilise "tel quel". Pas d'OS, pas de logique supérieure, rien ! Donc, pas besoin d'header.</p></li>
</ul><h3 id="toc_2">Rentrons dans le code</h3>
<p>J'ai divisé mon assembleur en 3 parties :</p>
<ul><li>Le parser/lexer : réalisé avec yacc pour le premier. On vérifie la grammaire et la sémantique afin de générer une liste d'instructions sous la forme d'un tableau de structures ainsi qu'une map faisant la conversion nom des label => adresse.</li>
<li>Le binder : transforme chaque instruction du tableau précédent en une instruction binaire à proprement parler (avec vérification en fonction du contexte)</li>
<li>Le main : qui wrap ces deux parties, les connecte entre elles et gère les options fournies par la ligne de commande.</li>
</ul><h3 id="toc_3">Et Go dans tout ça ?</h3>
<p>Il convient avant tout de parler un peu de la philosophie de Go avant d'aller plus loin.<br />
En effet, l'idée est de fournir des outils tout prêts à l'utilisateur. On se base sur un maximum de règles standards et donc un minimum de configuration (voire en fait pas de configuration du tout dans bien des cas !).</p>
<h4 id="toc_4">Le makefile</h4>
<p>L'exemple le plus frappant de ce concept est le makefile. Si dans les versions antérieures à la 1.0, Go possédait un simili makefile (il était déjà beaucoup plus simple qu'un makefile typique pour du C), tout cela est révolu !<br />
Maintenant un projet Go n'a besoin pour compiler que de ses sources. Un coup de "go build" et tout se fait tout seul. Plus de gestion des dépendances, plus de problèmes de conflits d'includes (de toute façon, il n'y a pas d'include en Go)… voilà qui devrait intéresser, je pense, tous ceux qui se sont essayés à C++ et à ses célèbres erreurs de compilation hyper-verbeuses, pour cause de conflit de define pour avoir placé un include au mauvais endroit.</p>
<p>Petite remarque tout de même : Mon projet contient un makefile !<br />
Bien que minimaliste, celui-ci est donc toujours présent.<br />
La raison est multiple :</p>
<ul><li><p>La commande "go build" construit votre binaire… et c'est tout ! J'aime pouvoir automatiser la génération de tarball, le nettoyage du projet, etc.</p></li>
<li><p>Mon projet utilise yacc (donc conversion du fichier parser.y en parser.go). De fait, go build ne met pas à jours parser.go si parser.y est mis à jour. D'où la nécessité de gérer cette dépendance</p></li>
</ul><h4 id="toc_5">Les tests</h4>
<p>De la même manière, réaliser des tests est simplissime. Pour écrire des tests pour le fichier foo.go, vous n'avez qu'à créer le fichier foo_test.go et… c'est tout ! Ce fichier se fera automatiquement compiler et ses fonctions commençant par Test seront exécutées à chaque lancement de "go test".</p>
<p>Dans le fichier, on importe le package "testing", et on appelle la méthode testing.T.Fail() ou testing.T.Error("C'est la dèche !") pour signaler que le test a échoué :</p>
<pre>
<code class="go"><span class="k">import</span> <span class="s">"testing"</span>
<span class="k">func</span> <span class="n">TestFoo</span><span class="p">(</span><span class="n">t</span> <span class="p">*</span><span class="n">testing</span><span class="p">.</span><span class="n">T</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Les fonctions de test commencent par Test</span>
<span class="c1">// et respectent cette signature</span>
<span class="k">if</span> <span class="n">test_is_ok</span><span class="p">()</span> <span class="p">!=</span> <span class="n">nil</span> <span class="p">{</span>
<span class="n">t</span><span class="p">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"t'es parti pour fixer ton code !"</span><span class="p">)</span> <span class="c1">// Erreur avec message</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">test_sans_message</span> <span class="p">!=</span> <span class="n">nil</span> <span class="p">{</span>
<span class="n">t</span><span class="p">.</span><span class="n">Fail</span><span class="p">()</span> <span class="c1">// Erreur sans message</span>
<span class="p">}</span>
<span class="c1">// Si aucun appel à Fail ou Error, alors le test est considéré comme réussit</span>
<span class="p">}</span>
</code>
</pre><h4 id="toc_6">Le multiplatforme !</h4>
<p>Encore une très bonne nouvelle : Go est multiplatforme de base !<br />
Voulant partager mon logiciel avec mes petits camarades (Je précise que je suis en Corée du Sud actuellement… pas la peine de dire quel OS utilise tout ce joli monde !), autant dire que cette fonctionnalité a fortement pesé dans la balance pour le choix de Go.</p>
<p>En outre, selon la doc il est possible de cross-compiler à partir de n'importe quelle platforme pour n'importe quelle autre juste en changeant ses variables locales comme GOOS ou GOARCH (et bien-sûr en compilant sa chaîne de compilation pour l'architecture cible). Toutefois, je n'ai pas testé cette possibilité, j'ai préféré rebooter sous windows (à ma décharge, j'aurais de toute façon dû le faire pour vérifier que mon binaire fonctionne bien !)</p>
<p>J'ai toutefois trouvé un peu bizarre que la gestion du retour à la ligne ne soit pas fournie comme en C++ (avec std::endl). De fait, on doit faire attention à ce léger détail et différencier les cas selon les OS à la main, ce qui est assez dommage.</p>
<h4 id="toc_7">Les outils en plus</h4>
<p>En bonus, go fourni des outils des plus sympa :</p>
<ul><li><p>go fmt<br />
Cette commande permet de mettre à LA norme le code. Notez le "LA" majuscule, il n'y en a qu'une (certains diront qu'elle est horrible mais nous ne sommes pas vendredi, je laisse cela à d'autres). Du coup, pas de conflits à ce niveau, tous les codes go écrits pas tous les développeurs du monde auront la même forme.<br />
Petit bémol pour ma part : cette norme utilise des indentations de 8 caractères et ne coupe pas le code à 80 colonnes. Résultat, celui-ci est bien souvent trop long à mon goût (ainsi qu'à celui de mon Emacs en multicolonnes. Là où je peux mettre 3 colonnes en C la plupart du temps, je suis limité à 2 en go… 33% d'espace perdu, snif !)</p></li>
<li><p>go tool yacc<br />
La commande go tool permet d'accéder à la foultitude d'outils intégré dans la commande go. Parmi eux se trouve yacc, le célèbre parser. Cette version est une réécriture en go de celui de plan9. Autant dire que sa présence à été déterminante dans mon choix d'utiliser go pour mon projet (un parser à la main… non merci !)</p></li>
</ul><h3 id="toc_8">Écrivons un peu de code</h3>
<p>Après tout ce temps à parler des outils, parlons du code, du vrai !</p>
<h4 id="toc_9">Le retour de plusieurs variables</h4>
<p>En voilà une bonne idée ! Toute fonction peut renvoyer plusieurs variables au lieu d'une seule dans la plupart des langages.<br />
De fait, on retrouve dans la lib standard de Go un bon nombre de fonctions renvoyant à la fois la valeur qu'on leur demande ainsi qu'un type "*Error" pouvant être soit "nil" (c'est-à-dire pointer sur rien) soit initialisé, signifiant alors une erreur. Plus besoin de "tricher" - comme en C - en donnant en argument de la fonction un pointeur sur la variable à compléter, puisque la variable de retour est déjà occupée par le code d'erreur.<br />
De même, le parcours de tableau s'en trouve simplifié :</p>
<pre>
<code class="go"><span class="k">for</span> <span class="n">i</span><span class="p">,</span><span class="n">elm</span> <span class="p">:=</span> <span class="k">range</span> <span class="n">array</span> <span class="p">{</span>
<span class="n">fmt</span><span class="p">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"l'élément"</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="s">"a pour valeur"</span><span class="p">,</span> <span class="n">elm</span><span class="p">)</span>
<span class="p">}</span>
</code>
</pre>
<p>range est un mot clé permettant de parcourir un tableau en renvoyant, à chaque itération, la position ainsi que l'élément courant.<br />
Notez aussi le ":=" permettant de déclarer "à l'arrache" des variable en fonction du type de la variable qu'on lui assigne. Un vrai bonheur pour gagner en lisibilité dans les cas triviaux.</p>
<h4 id="toc_10">La gestion des options</h4>
<p>Encore une bonne nouvelle ! La gestion des passages d'options étant une fonctionnalité essentielle à 90% des logiciels, le package "flag" s'occupe de cela avec brio !</p>
<pre>
<code class="go"><span class="k">import</span> <span class="s">"flag"</span>
<span class="k">var</span> <span class="n">f_input</span> <span class="p">=</span> <span class="n">flag</span><span class="p">.</span><span class="n">String</span><span class="p">(</span><span class="s">"i"</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="s">"Input file."</span><span class="p">)</span>
<span class="k">var</span> <span class="n">f_output</span> <span class="p">=</span> <span class="n">flag</span><span class="p">.</span><span class="n">String</span><span class="p">(</span><span class="s">"o"</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="s">"Output file. (stdout if nothing specified)"</span><span class="p">)</span>
<span class="k">var</span> <span class="n">f_type</span> <span class="p">=</span> <span class="n">flag</span><span class="p">.</span><span class="n">String</span><span class="p">(</span><span class="s">"t"</span><span class="p">,</span> <span class="s">"vhdl"</span><span class="p">,</span> <span class="s">"Type of output : binary, print, vhdl"</span><span class="p">)</span>
<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">flag</span><span class="p">.</span><span class="n">Parse</span><span class="p">()</span>
<span class="n">fmt</span><span class="p">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"lecture du fichier"</span><span class="p">,</span> <span class="p">*</span><span class="n">f_input</span><span class="p">)</span>
<span class="p">...</span>
<span class="p">}</span>
</code>
</pre>
<p>Ai-je vraiment besoin d'expliciter ? On déclare les flags en dehors des fonctions, on appel flag.Parse() avant d'utiliser les flags (notez qu'il s'agit de pointeurs, on met donc une "*" pour les déréférencer)<br />
Remarquez que l'option --help/-h est gérée automatiquement !</p>
<h4 id="toc_11">Les conteneurs</h4>
<p>Go possède de manière intégrée au langage les conteneurs les plus utilisés :</p>
<h5 id="toc_12">Map</h5>
<p>Son nom est suffisamment explicite : une clé, une valeur.<br />
Problème, selon moi : si la clé ne correspond à aucune valeur, la valeur nulle est renvoyée. De fait, dans mon programme, j'utilise une map pour faire la correspondance entre les labels et leur adresse réelle. Si je demande l'adresse d'un label inexistant, la map va me renvoyer la valeur 0, puisque c'est l'équivalent de la valeur nulle pour un int…<br />
Problème : cette valeur peu tout à fait être valide dans le cas d'un label situé en début de code ! C'est d'autant plus étrange que le langage autorise le renvoi de plusieurs valeurs, comme nous l'avons vu précédemment…</p>
<h5 id="toc_13">Array</h5>
<p>Un joli tableau unidimensionnel de taille constante… rien à redire.</p>
<h5 id="toc_14">Slice</h5>
<p>Un type fourre-tout : c'est un genre de tableau à taille variable.<br />
En réalité, le slice - comme son nom l'indique - représente un morceau de Array. De fait, plusieurs slices peuvent pointer en même temps sur le même Array, par exemple.</p>
<p>Je cherchais à utiliser un vecteur pour stocker chacune de mes instructions une fois parsées/lexées. J'ai eu la surprise de voir que le conteneur Vecteur avait été supprimé de la bibliothèque standard il y a quelques commits…<br />
La réponse s'est trouvée sur la mailling list de Go : utiliser des slices !<br />
Voici comment faire :</p>
<pre>
<code class="go"><span class="k">var</span> <span class="n">s0</span> <span class="p">:=</span> <span class="p">[]</span><span class="nb">int</span><span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">}</span> <span class="c1">// on créé un un slice sur un tableau contenant 0 et 1</span>
<span class="k">var</span> <span class="n">to_insert</span> <span class="p">=</span> <span class="mi">42</span>
<span class="n">S0</span> <span class="p">=</span> <span class="n">append</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">to_insert</span><span class="p">)</span> <span class="c1">// on utilise la fonction buildtin "append"</span>
</code>
</pre>
<p>Simple ? Bon maintenant, voyons comment supprimer l'élément numéro i (en C++ j'aurais fait "vect.remove(i)")</p>
<pre>
<code class="go"><span class="n">s0</span> <span class="p">=</span> <span class="n">append</span><span class="p">(</span><span class="n">s</span><span class="p">[:</span><span class="n">i</span> <span class="p">-</span> <span class="mi">1</span><span class="p">],</span> <span class="n">s</span><span class="p">[</span><span class="n">i</span> <span class="p">+</span> <span class="mi">1</span><span class="p">:]...)</span>
</code>
</pre>
<p>Pas glop ! Je pense qu'un peu de sucre syntaxique n'aurait pas été de trop pour cacher cette complexité inutile !<br />
Pour ceux qui se posent la question, append ajoute des éléments à un slice. De fait, on doit transformer le slice s[i + 1:] en une suite d'élément avant de l'ajouter à s[:i - 1]. C'est ce qu'on fait avec la commande "s[i + 1:]…"</p>
<h4 id="toc_15">L'héritage</h4>
<p>Dans Go l'héritage dans la grande tradition OO n'existe pas ! (et ce n'est pas pour me déplaire à titre personnel).<br />
Le tout s'articule autour de trois concepts (que je n'illustrerai pas avec mon programme puisse que celui-ci n'en n'utilise pas le premier.)</p>
<h5 id="toc_16">Les structures</h5>
<p>Plutôt que de déclarer des classes, on crée des structures. Celles-ci pouvant gérer l'héritage d'une façon amusante :</p>
<pre>
<code class="go"><span class="k">type</span> <span class="n">plat</span> <span class="k">struct</span> <span class="p">{</span>
<span class="n">name</span> <span class="nb">string</span>
<span class="p">}</span>
<span class="k">type</span> <span class="n">choucroute</span> <span class="k">struct</span> <span class="p">{</span>
<span class="n">plat</span>
<span class="n">saucisses</span> <span class="nb">int</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">ch</span> <span class="p">:=</span> <span class="n">choucroute</span><span class="p">{</span><span class="n">plat</span><span class="p">{</span><span class="s">"choucroute"</span><span class="p">},</span> <span class="mi">4</span><span class="p">}</span>
<span class="n">fmt</span><span class="p">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"Je vais me tapper une"</span><span class="p">,</span> <span class="n">ch</span><span class="p">.</span><span class="n">name</span><span class="p">,</span> <span class="s">"avec "</span><span class="p">,</span> <span class="n">ch</span><span class="p">.</span><span class="n">saucisses</span><span class="p">,</span> <span class="s">"saucisses dedans !"</span><span class="p">)</span>
<span class="c1">// ces deux lignes sont rigoureusement identiques</span>
<span class="n">fmt</span><span class="p">.</span><span class="n">Println</span><span class="p">(</span><span class="n">ch</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
<span class="n">fmt</span><span class="p">.</span><span class="n">Println</span><span class="p">(</span><span class="n">ch</span><span class="p">.</span><span class="n">plat</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
<span class="p">}</span>
</code>
</pre>
<p>On remarque donc que, en ajoutant dans choucroute une structure plat de manière anonyme (le nom "_" l'équivalent en Go de John Doe…), les éléments de plat sont intégrés dans choucroute !</p>
<h5 id="toc_17">Les méthodes</h5>
<p>Une fois notre jolie structure déclarée, il est possible de lui adjoindre des méthodes :</p>
<pre>
<code class="go"><span class="k">func</span> <span class="p">(</span><span class="n">ch</span> <span class="n">Choucroute</span><span class="p">)</span><span class="n">manger</span><span class="p">()</span> <span class="p">{</span>
<span class="n">ch</span><span class="p">.</span><span class="n">saucisses</span><span class="p">--</span>
<span class="n">fmt</span><span class="p">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"Miam ! Encore"</span><span class="p">,</span> <span class="n">ch</span><span class="p">.</span><span class="n">saucisses</span><span class="p">,</span> <span class="s">"saucisses !"</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">_</span> <span class="n">Plat</span><span class="p">)</span><span class="n">manger</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// remarquez le "_" pour indiquer qu'on ne fera rien avec l'objet et qu'il n'est donc pas nécessaire de le nommer.</span>
<span class="n">fmt</span><span class="p">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"Beark !"</span><span class="p">)</span>
<span class="p">}</span>
</code>
</pre><h5 id="toc_18">Les interfaces</h5>
<p>En Go, tout se base sur du Duck typing. De fait, afin de pouvoir manger à la fois un plat de seconde zone ou de la délicieuse choucroute, on peut déclarer une interface qui contiendra la fonction manger :</p>
<pre>
<code class="go"><span class="k">type</span> <span class="n">mangerer</span> <span class="k">interface</span> <span class="p">{</span>
<span class="n">manger</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">bouffe</span> <span class="p">:=</span> <span class="p">[]</span><span class="n">mangerer</span>
<span class="n">bouffe</span> <span class="p">=</span> <span class="n">append</span><span class="p">(</span><span class="n">bouffe</span><span class="p">,</span> <span class="n">choucroute</span><span class="p">{</span> <span class="s">"choucroute"</span><span class="p">,</span> <span class="mi">4</span> <span class="p">})</span>
<span class="n">bouffe</span> <span class="p">=</span> <span class="n">append</span><span class="p">(</span><span class="n">bouffe</span><span class="p">,</span> <span class="n">plat</span><span class="p">{</span> <span class="s">"bouts de tétons de mme Félipé"</span> <span class="p">})</span>
<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">pl</span> <span class="p">:=</span> <span class="k">range</span> <span class="n">bouffe</span> <span class="p">{</span>
<span class="n">pl</span><span class="p">.</span><span class="n">manger</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code>
</pre>
<p>De fait, le duck typing faisant son travail, notre slice peut contenir n'importe quelle structure possédant une fonction ayant pour signature "manger()".</p>
<p>Au final, un gain de légèreté monstrueux comparé au C++ et à ses déclarations d'objets et d'héritage particulièrement verbeuses.</p>
<p>C'est beau, c'est simple, ça me fait pleurer !</p>
<h4 id="toc_19">Les autres fonctionnalités</h4>
<h5 id="toc_20">Les goroutines</h5>
<p>Bien sûr, je n'ai pas pu tout tester dans mon programme.<br />
Je pense notamment à une des principales fonctionnalités mise en avant : la concurrence.<br />
Mon programme n'utilise qu'un fil d'exécution. De fait, je n'ai pas pu utiliser les channels ni le mot clé go.<br />
Toutefois, ayant déjà fait un peu joujou avec par le passé, j'ai trouvé l'idée vraiment excellente. Les channels permettant à une routine d'attendre qu'une autre lui envoie un objet pour continuer. De fait, on résout le soucis de synchronisation de manière beaucoup plus simple qu'en plaçant des mutex sur les ressources critiques !</p>
<h2 id="toc_21">Conclusion</h2>
<p>Je suis globalement très satisfait de Go, j'ai l'impression d'un langage rapide, et ce dans tous les sens du terme :</p>
<ul><li><p>Rapidité de compilation.<br />
Quasi instantanée ! (dois-je comparer à un poid lourd comme C++ ?)</p></li>
<li><p>Rapidité de développement.<br />
Tout va très vite. Le duck typing permet de redéfinir ses structures très rapidement, on n'écrit que le minimum.<br />
De même, tout le monde est convaincu de l'importance des tests, mais la flemme nous fait généralement (en particulier pour les petits projet comme le mien) tester à la main la fonctionnalité sur laquelle on travaille actuellement et basta !<br />
De fait, le système de test intégré à Go est, pour moi, un pur bonheur, tant il est simple et efficace !</p></li>
<li><p>Rapidité d'exécution.<br />
Le débat est lancé ! Pour certains, le compilateur de Go est trop jeune et pas suffisamment optimisé (ce qui explique sa vitesse de compilation).<br />
Les auteurs de Go ont notamment <a href="http://blog.golang.org/2011/06/profiling-go-programs.html">publié un article</a> pour battre en brèche cette idée en montrant comment obtenir des performances proches du C++ en optimisant son code.</p></li>
</ul><p>D'un autre côté, il s'agit d'un langage jeune, avec tous les problèmes inhérents :</p>
<ul><li><p>Peu de bibliothèques tierces pour le moment. Au vu de la simplicité d'interfaçage de Go avec C, des projets de bindings de grosses bibliothèques fleurissent un peu partout, mais pour le moment rien de très stable/utilisable.</p></li>
<li><p>Un compilateur jeune.<br />
En effet, pour le moment le runtime est compilée statiquement dans le logiciel. Cela explique la taille supérieure au mégaoctet du moindre "hello world". Par exemple, mon logiciel faisant dans les 800 lignes de Go se retrouve compilé dans un binaire de 1.6 Mo…<br />
Enfin bon, vue la taille actuelle de nos disques durs et tant que Go n'aura pas pour vocation de tourner sur de l'embarqué, je ne suis pas sûr que ce soit un si gros point noir.</p></li>
</ul></div><div><a href="https://linuxfr.org/news/retour-d-experience-sur-go.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/94147/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/retour-d-experience-sur-go#comments">ouvrir dans le navigateur</a>
</p>
G.bleubaud123claudexNÿcoB16F4RV4RD1Nhttps://linuxfr.org/nodes/94147/comments.atomtag:linuxfr.org,2005:Diary/325922012-05-16T08:49:03+02:002012-05-16T15:24:52+02:00Retour d'expérience sur GoLicence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<h2 id="sommaire">Sommaire</h2>
<ul><li>
<a href="#toc_0">Mise en situation :</a>
</li>
<li>
<a href="#toc_1">Le programme :</a>
<ul><li>
<a href="#toc_2">Rentrons dans le code</a>
</li>
<li>
<a href="#toc_3">Et Go dans tout ça ?</a>
<ul><li>
<a href="#toc_4">Le makefile</a>
</li>
<li>
<a href="#toc_5">Les tests</a>
</li>
<li>
<a href="#toc_6">Le multiplatforme !</a>
</li>
<li>
<a href="#toc_7">Les outils en plus</a>
</li>
</ul></li>
<li>
<a href="#toc_8">Écrivons un peu de code</a>
<ul><li>
<a href="#toc_9">Le retour de plusieurs variables</a>
</li>
<li>
<a href="#toc_10">La gestion des options</a>
</li>
<li>
<a href="#toc_11">Les conteneurs</a>
<ul><li>
<a href="#toc_12">Map</a>
</li>
<li>
<a href="#toc_13">Array</a>
</li>
<li>
<a href="#toc_14">Slice</a>
</li>
</ul></li>
<li>
<a href="#toc_15">L'héritage</a>
<ul><li>
<a href="#toc_16">les structures</a>
</li>
<li>
<a href="#toc_17">Les méthodes</a>
</li>
<li>
<a href="#toc_18">Les interfaces</a>
</li>
</ul></li>
<li>
<a href="#toc_19">Les autres fonctionnalités</a>
<ul><li>
<a href="#toc_20">les goroutines</a>
</li>
</ul></li>
</ul></li>
</ul></li>
<li>
<a href="#toc_21">Conclusion</a>
</li>
</ul><p>NdM : <em>Ce journal a été <a href="http://linuxfr.org/news/retour-d-experience-sur-go">promu en dépêche</a></em></p>
<p>Bonjour au journal et à la famille.</p>
<p>Je viens de finir un petit projet en Go la semaine dernière. Voici un petit retour d'expérience en espérant que ça serve !</p>
<h2 id="toc_0">Mise en situation :</h2>
<p>Pour mes études j'ai un projet (le dernier avant la vie active !) de réalisation de microprocesseur MIPS "from scratch".<br />
Le CPU est designé sous Xilinx ISE (grosso modo un IDE dans lequel on peut réaliser des designs de composants à base de portes logiques). Par la suite il sera chargé sur un fpga, celui-ci connecté à un robot afin de le faire suivre une ligne sur le sol.</p>
<p>De fait une fois le CPU designé, il faut réaliser un programme en assembleur MIPS, puis le convertir en binaire afin de l'intégrer dans le design du CPU sous la forme d'un module en vhdl. (en gros en entrée du module arrive l'adresse du program counter et le module sort l'instruction correspondante)</p>
<p>Les plus attentifs auront déjà pointé du doigt le soucis : comment convertir proprement le code assembleur MIPS en binaire ?</p>
<ul><li><p>À la main<br />
Ne riez pas, c'est ce que m'a proposé mon prof quand je lui ai posé la question ! (à sa décharge, les élèves suivant ce cours ne sont pas informaticiens mais plutôt orientés électronique)</p></li>
<li><p>Utiliser un assembleur déjà existant<br />
La solution "ne pas réinventer la roue" de référence. Le problème : l'output sera en binaire (logique me direz-vous) mais je veux du code binaire lisible ! (en gros mon output doit être 000101011100110 afin de pouvoir directement copier coller le code dans le fichier de mon module vhddl)<br />
Ajouter à cela que je ne veux pas de header ELF ou quoi que ce soit, juste la transcription du code que j'ai écrit.<br />
Je pense bien sûr qu'il y a des solutions pour arriver à ce que je veux. Néanmoins j'ai du temps en ce moment et apprendre un nouveau langage me semble plus formateur qu'apprendre les options d'un outil qui ne me servira plus par la suite !</p></li>
<li><p>Écrire un assembleur à la main<br />
La solution que j'ai choisi (et qui me donne donc la possibilité de réaliser ce journal !). Un assembleur n'est pas aussi complexe qu'un compilateur (et de loin !) mais permet déjà de s'amuser sur un nouveau langage.</p></li>
</ul><h2 id="toc_1">Le programme :</h2>
<p>Linus Torvald :</p>
<blockquote>
<p>Show me the code !</p>
</blockquote>
<p><a href="http://sourceforge.net/projects/gopiler">Céans mon bon monsieur !</a>. En espérant que tout le monde aime sourceforge…</p>
<p>Comme dit plus haut, il s'agit d'un assembleur MIPS "simplifié" :</p>
<ul><li><p>Toute les instructions ne sont pas disponibles (pas de jump ou de subi par exemple). La raison est tout simplement que mon processeur ne supporte pas ces instructions et je préfère avoir une erreur à la compilation si j'oublie ce détail que de devoir débugger une erreur qui n'en est pas une par la suite… Malgré tout il est très simple d'ajouter ces fonctionnalités comme nous allons le voir.</p></li>
<li><p>Pas de header ELF pour le programme. Comme j'ai dit, l'idée et de copier la sortie de l'assembleur dans un fichier pour que mon cpu l'utilise "tel quel". Pas d'OS, pas le logique supérieur, rien ! Donc pas besoin d'header.</p></li>
</ul><h3 id="toc_2">Rentrons dans le code</h3>
<p>J'ai divisé mon assembleur en 3 parties :<br />
- Le parser/lexer : Réaliser avec yacc pour le premier. On vérifie la grammaire et la sémantique afin de générer une liste d'instructions sous la forme d'un tableau de structures ainsi qu'une map faisant la conversion nom des label => adresse.<br />
- Le binder : transforme chaque instruction du tableau précédent en une instruction binaire à proprement parlé (avec vérification en fonction du contexte)<br />
- Le main : qui wrap ces deux parties, les connecte entre elles et gère les options fournies par la ligne de commande.</p>
<h3 id="toc_3">Et Go dans tout ça ?</h3>
<p>Il convient avant tout de parler un peu de la philosophie de Go avant d'aller plus loin.<br />
En effet l'idée est de fournir des outils tout prêt à l'utilisateur. On se base sur un maximum de règles standards et donc un minimum de configuration (voir en fait pas de configuration du tout dans bien des cas !).</p>
<h4 id="toc_4">Le makefile</h4>
<p>L'exemple le plus frappant de ce concept est le makefile. Si dans les versions antérieurs à la 1.0, Go possédé un simili makefile (il était déjà beaucoup plus simple qu'un makefile typique pour du C), tout cela est révolu !<br />
Maintenant un projet Go n'a besoin pour compiler que de ses sources. Un coup de "go build" et tout se fait tout seul. Plus de gestions des dépendances, plus de problèmes de conflits d'includes (de toute façon, il n'y a pas d'include en Go)… voila qui devrait intéresser, je pense, tout ceux qui se sont essayé à C++ et a ses célèbres erreurs de compilation hyper verbeuses pour cause de conflit de define pour avoir placé un include au mauvais endroit.</p>
<p>Petite remarque tout de même : Mon projet contient un makefile !<br />
Bien que minimaliste, celui-ci est donc toujours présent.<br />
La raison est multiple :</p>
<ul><li><p>La commande "go build" construit votre binaire… et c'est tout ! J'aime pouvoir automatiser la génération de tarball, le nettoyage do projet etc…</p></li>
<li><p>Mon projet utilise yacc (donc conversion du fichier parser.y en parser.go). De fait, go build ne met pas à jours parser.go si parser.y est mis à jour. d'où la nécessité de gérer cette dépendance</p></li>
</ul><h4 id="toc_5">Les tests</h4>
<p>De la même manière, réaliser des tests est simplissime. Pour écrire des tests pour le fichier foo.go vous n'avez qu'à créer le fichier foo_test.go et… c'est tout ! Ce fichier se fera automatiquement compiler ses fonctions commençant par Test seront exécuté à chaque lancement de "go test".</p>
<p>Dans le fichier, on import le package "testing", et on appel la méthode testing.T.Fail() ou testing.T.Error("C'est la dèche !") pour signaler que le test a échoué :</p>
<pre>
<code class="">import "testing"
func TestFoo(t *testing.T) { // Les fonctions de test commencent par Test
// et respectent cette signature
if test_is_ok() != nil {
t.Error("t'es parti pour fixer ton code !") // Erreur avec message
}
if test_sans_message != nil {
t.Fail() // Erreur sans message
}
// Si aucun appel à Fail ou Error, alors le test est considéré comme réussit
}
</code>
</pre><h4 id="toc_6">Le multiplatforme !</h4>
<p>Encore une très bonne nouvelle : Go est multiplatforme de base !<br />
Voulant partager mon logiciel avec mes petits camarades (Je précise que je suis en Corée du Sud actuellement… pas la peine de dire quel OS utilise tout ce joli monde !), autant dire que cette fonctionnalité a fortement pesé dans la balance pour le choix de Go.</p>
<p>De plus, selon la doc il est possible de cross-compiler à partir de n'importe quelle platforme pour n'importe quelle autre juste en changeant ses variables locales comme GOOS ou GOARCH (et bien sûr en compilant sa chaîne de compilation pour l'architecture cible). Toutefois je n'ai pas testé cette possibilité, j'ai préféré rebooter sous windows (à ma décharge, j'aurai de toute façon dû le faire pour vérifier que mon binaire marche bien !)</p>
<p>J'ai toutefois trouvé un peu bizarre que la gestion du retour à la ligne ne soit pas fournie comme en C++ (avec std::endl). De fait on doit faire attention à ce léger détail et différencier les cas selon les OS à la main ce qui est assez dommage.</p>
<h4 id="toc_7">Les outils en plus</h4>
<p>En bonus, go fourni des outils des plus sympa :</p>
<ul><li><p>go fmt<br />
Cette commande permet de mettre à LA norme le code. Notez le "LA" majuscule, il n'y en a qu'une (certains diront qu'elle est horrible mais nous ne sommes pas vendredi, je laisse cela à d'autres). Du coup, pas de conflits à ce niveau, tous les codes go écris pas tous les développeurs du monde auront la même forme.<br />
Petit bémol pour ma part : cette norme utilise des indentations de 8 caractères et ne coupe pas le code à 80 colonnes. Résultat celui-ci est bien souvent trop long à mon goût (ainsi qu'à celui de mon Emacs en multicolonnes. Là où je peux mettre 3 colonnes en C la plupart du temps, je suis limité à 2 en go… 33% d'espace perdu, snif !)</p></li>
<li><p>go tool yacc<br />
La commande go tool permet d'accéder à la foultitude d'outils intégré dans la commande go. Parmi eux se trouve yacc, le célèbre parser. Cette version est une réécriture en go de celui de plan9. Autant dire que sa présence à été déterminante dans mon choix d'utiliser go pour mon projet (un parser à la main… non merci !)</p></li>
</ul><h3 id="toc_8">Écrivons un peu de code</h3>
<p>Après tout ce temps à parler des outils, parlons du code, du vrai !</p>
<h4 id="toc_9">Le retour de plusieurs variables</h4>
<p>En voila une bonne idée ! Toute fonction peut renvoyer plusieurs variables au lieu d'une seule dans la plupart des langages.<br />
De fait on retrouve dans la lib standard de Go un bon nombre de fonctions renvoyant à la fois la valeur qu'on leur demande ainsi qu'un type "*Error" pouvant être soit "nil" (c'est à dire pointer sur rien) soit initialiser, signifiant alors une erreur. Plus besoins de "tricher" comme en C en donnant en argument de la fonction un pointer sur la variable à compléter puisque la variable de retour est déjà occupé par le code d'erreur.<br />
De même le parcours de tableau s'en trouve simplifié :</p>
<pre>
<code class="">for i,elm := range array {
fmt.Println("l'élément", i, "a pour valeur", elm)
}
</code>
</pre>
<p>range est un mot clé permettant de parcourir un tableau en renvoyant à chaque itération la position ainsi que l'élément courant.<br />
Notez aussi le ":=" permettant de déclarer "à l'arrache" des variable en fonction du type de la variable qu'on lui assigne. Un vrai bonheur pour gagner en lisibilité dans les cas triviaux.</p>
<h4 id="toc_10">La gestion des options</h4>
<p>Encore une bonne nouvelle ! La gestions des passages d'options étant une fonctionnalité essentiel à 90% des logiciels, le package "flag" s'occupe de cela avec brio !</p>
<pre>
<code class="">import "flag"
var f_input = flag.String("i", "", "Input file.")
var f_output = flag.String("o", "", "Output file. (stdout if nothing specified)")
var f_type = flag.String("t", "vhdl", "Type of output : binary, print, vhdl")
func main() {
flag.Parse()
fmt.Println("lecture du fichier", *f_input)
...
}
</code>
</pre>
<p>Ai-je vraiment besoin d'expliciter ? On déclare les flags en dehors des fonctions, on appel flag.Parse() avant d'utiliser les flags (notez qu'il s'agit de pointers, on met donc une "*" pour les déférencer)<br />
Remarquez que l'option --help/-h est gérée automatiquement !</p>
<h4 id="toc_11">Les conteneurs</h4>
<p>Go possède de manière intégré au langage les conteneurs les plus utilisés :</p>
<h5 id="toc_12">Map</h5>
<p>Son nom est suffisamment explicite : une clé, une valeur.<br />
Problème selon moi : si la clé ne correspond à aucune valeur, la valeur nulle est renvoyée. De fait dans mon programme j'utilise une map pour faire la correspondance entre les labels et leur adresse réelle. Si je demande l'adresse d'un label inexistant, la map va me renvoyer la valeur 0 puisque c'est l'équivalent de la valeur nulle pour un int…<br />
Problème : cette valeur peu tout à fait être valide dans le cas d'un label situé en début de code ! C'est d'autant plus étrange que le langage autorise le renvoie de plusieurs valeurs comme nous l'avons vu précédemment…</p>
<h5 id="toc_13">Array</h5>
<p>Un jolie tableau unidimensionnel de taille constante… rien à redire.</p>
<h5 id="toc_14">Slice</h5>
<p>Un type four tout : c'est un genre de tableau à taille variable.<br />
En réalité le slice, comme son nom l'indique, représente un morceau de Array. De fait plusieurs slices peuvent pointer en même temps sur le même Array par exemple.</p>
<p>Je cherchais à utiliser un vecteur pour stocker chacun de mes instructions une fois parsées/lexées. J'ai eu la surprise de voir que le conteneur Vecteur avait été supprimé de la lib standard il y a quelques commits…<br />
La réponse s'est trouvé sur la mailling list de Go : utiliser des slices !<br />
Voici comment faire :</p>
<pre>
<code class="">var s0 := []int{0, 1} // on créé un un slice sur un tableau contenant 0 et 1
var to_insert = 42
S0 = append(s, to_insert) // on utilise la fonction buildtin "append"
</code>
</pre>
<p>Simple ? bon maintenant voyons comment supprimer l'élément numéro i (en C++ j'aurais fait "vect.remove(i)")</p>
<pre>
<code class="">s0 = append(s[:i - 1], s[i + 1:]...)
</code>
</pre>
<p>Pas glop ! Je pense qu'un peu de sucre syntaxique n'aurais pas été de trop pour cacher cette complexité inutile !<br />
Pour ceux qui se posent la question, append ajoute des éléments à un slice. De fait on doit transformer le slice s[i + 1:] en une suit d'élément avant de l'ajouter à s[:i - 1]. C'est ce qu'on fait avec la commande "s[i + 1:]…"</p>
<h4 id="toc_15">L'héritage</h4>
<p>Dans Go l'héritage dans la grande tradition OO n'existe pas ! (et ce n'est pas pour me déplaire à titre personnel)<br />
De tout s'articule autour de trois concepts (que je n'illustrerais pas avec mon programme puisse que celui-ci n'en n'utilise pas le premier.)</p>
<h5 id="toc_16">les structures</h5>
<p>Plutôt que de déclarer des classes, on créé des structures. Celles-ci pouvant gérer l'héritage d'une façon amusante :</p>
<pre>
<code class="">type plat struct {
name string
}
type choucroute struct {
_ plat
saucisses int
}
func main() {
var ch choucroute = choucroute{ "choucroute", 4 }
fmt.Println("Je vais me tapper une", ch.name, "avec ", ch.saucisses, "saucisses dedans !")
}
</code>
</pre>
<p>On remarque donc que en ajoutant dans choucroute une structure plat de manière anonyme (le nom "_" l'équivalent en Go de John Doe…) les éléments de plat sont intégrés dans choucroute !</p>
<h5 id="toc_17">Les méthodes</h5>
<p>Une fois notre jolie structure déclarée, il est possible de lui adjoindre des méthodes :</p>
<pre>
<code class="">func (ch Choucroute)manger() {
ch.saucisses--
fmt.Println("Miam ! Encore", ch.saucisses, "saucisses !")
}
func (_ Plat)manger() { // remarquez le "_" pour indiquer qu'on ne fera rien avec l'objet et qu'il n'est donc pas nécessaire de le nommer.
fmt.Println("Beark !")
}
</code>
</pre><h5 id="toc_18">Les interfaces</h5>
<p>En Go, tout se base sur du Duck typing. De fait afin de pouvoir manger à la fois un plat de seconde zone où de la délicieuse choucroute, on peut déclarer une interface qui contiendra la fonction manger :</p>
<pre>
<code class="">type mangerer interface {
manger()
}
func main() {
bouffe := []mangerer
bouffe = append(bouffe, choucroute{ "choucroute", 4 })
bouffe = append(bouffe, plat{ "bouts de tétons de mme Félipé" })
for _, pl := range bouffe {
pl.manger()
}
}
</code>
</pre>
<p>De fait le duck typing faisant son travail, notre slice peut contenir n'importe quel structure possédant une fonction ayant pour signature "manger()".</p>
<p>Au final un gain de légèreté monstrueux comparé au C++ et à ses déclarations d'objets et d'héritage particulièrement verbeuses.</p>
<p>C'est beau, c'est simple, ça me fait pleurer !</p>
<h4 id="toc_19">Les autres fonctionnalités</h4>
<h5 id="toc_20">les goroutines</h5>
<p>Bien sûr, je n'ai pas pu tout tester dans mon programme.<br />
Je pense notamment à une des principales fonctionnalités mise en avant : la concurrence.<br />
Mon programme n'utilise qu'un fil d'exécution. De fait, je n'ai pas pu utiliser les channels ni le mot clé go.<br />
Toutefois, ayant déjà fait un peu joujou avec par le passé, j'ai trouvé l'idée vraiment exellente. Les channels permettant à une routine d'attendre qu'une autre lui envoie un objet pour continuer. De fait on résout le soucis de synchronisation de manière beaucoup plus simple qu'en plaçant des mutex sur les ressources critiques !</p>
<h2 id="toc_21">Conclusion</h2>
<p>Je suis globalement très satisfait de Go, j'en ai l'impression d'un langage rapide, et ce dans tous les sens du terme :</p>
<ul><li><p>Rapidité de compilation.<br />
Quasi instantanée ! (dois-je comparer à un poid lourd comme C++ ?)</p></li>
<li><p>Rapidité de développement.<br />
Tout va très vite. Le duck typing permet de redéfinir ses structures très rapidement, on n'écrit que le minimum.<br />
De même, Tout le monde est convaincu de l'importance des tests mais la flemme nous fait généralement (en particulier pour les petits projet comme le mien) tester à la main la fonctionnalité sur laquelle on travail actuellement et basta !<br />
De Fait, le système de test intégré à Go est pour moi un pur bonheur tant il est simple est efficace !</p></li>
<li><p>Rapidité d'exécution.<br />
Le débat est lancé ! Pour certain le compilateur de Go est trop jeune et pas suffisamment optimisé (ce qui explique sa vitesse de compilation).<br />
Les auteurs de Go ont notamment <a href="http://blog.golang.org/2011/06/profiling-go-programs.html">publié un article</a> pour battre en brêche cette idée en montrant comment obtenir des performances proches du C++ en optimisant son code.</p></li>
</ul><p>D'un autre côté, il s'agit d'un langage jeune, avec tous les problèmes inhérents :</p>
<ul><li><p>Peu de libs tiers pour le moment<br />
Au vu de la simplicité d'interfaçage de Go avec C, des projets de bindings de grosses libs fleurissent un peu partout mais pour le moment rien de très stable/utilisable</p></li>
<li><p>Un compilateur jeune.<br />
En effet, pour le moment la runtime est compilée statiquement dans le logiciel. Cela explique la taille supérieur au mégaoctet du moindre "hello world". Par exemple mon logiciel faisant dans les 800 lignes de Go se retrouve compilé dans un binaire de 1.6mo…<br />
Enfin bon vu la taille actuelle de nos disques durs et tant que Go n'aurra pas pour vocation de tourner sur de l'embarqué, je ne suis pas sûr que ce soit un si gros point noir.</p></li>
</ul><div><a href="https://linuxfr.org/users/g-bleu/journaux/retour-d-experience-sur-go.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/94145/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/g-bleu/journaux/retour-d-experience-sur-go#comments">ouvrir dans le navigateur</a>
</p>
G.bleuhttps://linuxfr.org/nodes/94145/comments.atom