Journal Retour d'expérience sur Go

Posté par  (site web personnel) . Licence CC By‑SA.
65
16
mai
2012

Sommaire

NdM : Ce journal a été promu en dépêche

Bonjour au journal et à la famille.

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 !

Mise en situation :

Pour mes études j'ai un projet (le dernier avant la vie active !) de réalisation de microprocesseur MIPS "from scratch".
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.

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)

Les plus attentifs auront déjà pointé du doigt le soucis : comment convertir proprement le code assembleur MIPS en binaire ?

  • À 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)

  • Utiliser un assembleur déjà existant
    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)
    Ajouter à cela que je ne veux pas de header 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 !

  • Écrire un assembleur à la main
    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.

Le programme :

Linus Torvald :

Show me the code !

Céans mon bon monsieur !. En espérant que tout le monde aime sourceforge…

Comme dit plus haut, il s'agit d'un assembleur MIPS "simplifié" :

  • 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.

  • 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.

Rentrons dans le code

J'ai divisé mon assembleur en 3 parties :
- 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.
- Le binder : transforme chaque instruction du tableau précédent en une instruction binaire à proprement parlé (avec vérification en fonction du contexte)
- Le main : qui wrap ces deux parties, les connecte entre elles et gère les options fournies par la ligne de commande.

Et Go dans tout ça ?

Il convient avant tout de parler un peu de la philosophie de Go avant d'aller plus loin.
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 !).

Le makefile

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 !
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.

Petite remarque tout de même : Mon projet contient un makefile !
Bien que minimaliste, celui-ci est donc toujours présent.
La raison est multiple :

  • 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…

  • 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

Les tests

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".

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é :

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
}

Le multiplatforme !

Encore une très bonne nouvelle : Go est multiplatforme de base !
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.

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 !)

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.

Les outils en plus

En bonus, go fourni des outils des plus sympa :

  • go fmt
    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.
    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 !)

  • go tool yacc
    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 !)

Écrivons un peu de code

Après tout ce temps à parler des outils, parlons du code, du vrai !

Le retour de plusieurs variables

En voila une bonne idée ! Toute fonction peut renvoyer plusieurs variables au lieu d'une seule dans la plupart des langages.
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.
De même le parcours de tableau s'en trouve simplifié :

for i,elm := range array {
       fmt.Println("l'élément", i, "a pour valeur", elm)
}

range est un mot clé permettant de parcourir un tableau en renvoyant à chaque itération la position ainsi que l'élément courant.
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.

La gestion des options

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 !

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)
...
}

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)
Remarquez que l'option --help/-h est gérée automatiquement !

Les conteneurs

Go possède de manière intégré au langage les conteneurs les plus utilisés :

Map

Son nom est suffisamment explicite : une clé, une valeur.
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…
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…

Array

Un jolie tableau unidimensionnel de taille constante… rien à redire.

Slice

Un type four tout : c'est un genre de tableau à taille variable.
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.

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…
La réponse s'est trouvé sur la mailling list de Go : utiliser des slices !
Voici comment faire :

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"

Simple ? bon maintenant voyons comment supprimer l'élément numéro i (en C++ j'aurais fait "vect.remove(i)")

s0 = append(s[:i - 1], s[i + 1:]...)

Pas glop ! Je pense qu'un peu de sucre syntaxique n'aurais pas été de trop pour cacher cette complexité inutile !
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:]…"

L'héritage

Dans Go l'héritage dans la grande tradition OO n'existe pas ! (et ce n'est pas pour me déplaire à titre personnel)
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.)

les structures

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 :

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 !")
}

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 !

Les méthodes

Une fois notre jolie structure déclarée, il est possible de lui adjoindre des méthodes :

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 !")
}

Les interfaces

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 :

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()
     }
}

De fait le duck typing faisant son travail, notre slice peut contenir n'importe quel structure possédant une fonction ayant pour signature "manger()".

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.

C'est beau, c'est simple, ça me fait pleurer !

Les autres fonctionnalités

les goroutines

Bien sûr, je n'ai pas pu tout tester dans mon programme.
Je pense notamment à une des principales fonctionnalités mise en avant : la concurrence.
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.
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 !

Conclusion

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 :

  • Rapidité de compilation.
    Quasi instantanée ! (dois-je comparer à un poid lourd comme C++ ?)

  • Rapidité de développement.
    Tout va très vite. Le duck typing permet de redéfinir ses structures très rapidement, on n'écrit que le minimum.
    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 !
    De Fait, le système de test intégré à Go est pour moi un pur bonheur tant il est simple est efficace !

  • Rapidité d'exécution.
    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).
    Les auteurs de Go ont notamment publié un article pour battre en brêche cette idée en montrant comment obtenir des performances proches du C++ en optimisant son code.

D'un autre côté, il s'agit d'un langage jeune, avec tous les problèmes inhérents :

  • Peu de libs tiers pour le moment
    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

  • Un compilateur jeune.
    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…
    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.

  • # Et les lib ?

    Posté par  . Évalué à 2.

    Très bon article, merci.

    Une question tout de même :

    Peu de libs tiers pour le moment
    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

    Et dans l'autre sens ? Est-il facile d'écrire une bibliothèque facile à piloter en C, l'ABI semble-t-elle définitive ?

  • # erratum

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

    Je viens de me rendre compte que la fatique aidant, j'ai écrit n'importe quoi dans la partie sur l'héritage (je sais, j'aurai dû tester mon code avant !), si un modo pouvait faire les correction…

    Pas de "_" avant le "plat" dans la déclaration de la structure

    type choucroute struct {
        plat
        saucisses int
    }
    
    

    Par contre la création d'un type choucroute doit se faire comme ça :

    ch := choucroute{plat{"choucroute"}, 4}
    
    

    Ce n'est que par la suite qu'on peut oublier le type plat :

    // ces deux lignes sont rigoureusement identiques
    fmt.Println(ch.name)
    fmt.Println(ch.plat.name)
    
    

    Enfin la déclaration d'une interface se fait comme cela :

    bouffe := []mangerer{}
    
    

    Voila, pardon aux familles tout ça…

    • [^] # Re: erratum

      Posté par  . Évalué à 1.

      Un jour viendra où nous pourrons éditer les journaux sous LinuxFR voir même les commentaires. Ça prendra peut-être 10 ans de développement acharnés mais ils y arriveront!

  • # Abuser du sucre c'est mauvais pour la santé

    Posté par  . Évalué à 5.

    Simple ? bon maintenant voyons comment supprimer l'élément numéro i (en C++ j'aurais fait "vect.remove(i)")
    s0 = append(s[:i - 1], s[i + 1:]…)
    Pas glop ! Je pense qu'un peu de sucre syntaxique n'aurais pas été de trop pour cacher cette complexité inutile !

    vect.remove et l'opération que tu cites ne sont pas 100% identiques, dans un cas tu modifie le contenu d'un tableau existant dans l'autre tu alloue un nouveau tableau (à la mode fonctionnelle).
    J'imagine qu'ils n'ont pas ajouté ces opérations car elles sont couteuses..

  • # Über pertinent

    Posté par  . Évalué à 4.

    J'en parlais ce matin avec le concierge de la tribune. Le Duck typing est particulièrement adapté au développement de gocoincoin.

  • # Maps et slices

    Posté par  . Évalué à 5.

    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…

    Comme tu le dis, Go est capable de renvoyer plusieurs valeurs en retour de fonction.
    Pour savoir si la clé existe, il faut utiliser le paradigme, fréquent en Go, dit comma ok :

    if res, ok := map[key]; ok {
        return res
    }
    
    

    Pour les slices, il manque peut-être un peu de sucre syntaxique oui, mais il est simple de créer une fonction remove(i) qui agira comme souhaité.

    En tout cas sympathique retour et projet, je vais moi aussi utiliser Go pour mes développements à venir.

    • [^] # Re: Maps et slices

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

      res, ok := map[key]; ok 
      
      

      C'est très très moche quand même. C'est impossible de lire ce truc sans connaitre la priorité des 3 opérateurs.

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

      • [^] # Re: Maps et slices

        Posté par  . Évalué à 2.

        Je partage le même point de vue. J'aurais tendance à préférer la forme suivante, bien que demandant une ligne supplémentaire :

        val, present := map[key]
        if present {
            ...
        }
        
        

        Quant au fait de pouvoir lire ce bout code sans connaître Go, il est surtout lié au nom donné à la variable de vérification de présence (et à la connaissance du langage, mais c'est vrai pour tous).

      • [^] # Re: Maps et slices

        Posté par  . Évalué à 2.

        C’est un idiome assez courant dans ce langage, tu t’y fais très rapidement.

        • [^] # Re: Maps et slices

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

          Cela viole plein de règle habituel sur la force de "," et la présence de ";" à la ocaml plutôt que la version du C.

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

          • [^] # Re: Maps et slices

            Posté par  . Évalué à 3. Dernière modification le 16 mai 2012 à 14:55.

            Heu, en C "," est prioritaire devant ";"… exactement comme en Go.

            for(int i=0,j=0;i<width&&j<height;i++)
            
            

            Quant à OCaml je connais pas donc je ne saurai dire, mais la compatibilité de la syntaxe avec Caml n’est vraiment pas, mais alors pas du tout un objectif de Go.

            Edit: niveau priorité des opérateurs, le ";" dans le "if" est géré en Go très exactement comme le ";" dans le for en Go, qui est lui-même géré exactement comme le ";" dans le for en C. Je vois pas ce qu’il y a de choquant niveau lisibilité ou priorités habituelles.

            Pour expliciter, if a;b { … } est équivalent à for a;b; { …; break; }, qui existe en C. Rien de vraiment révolutionnaire ou traumatisant donc.

    • [^] # Re: Maps et slices

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

      Comme tu le dis, Go est capable de renvoyer plusieurs valeurs en retour de fonction.
      Pour savoir si la clé existe, il faut utiliser le paradigme, fréquent en Go, dit comma ok :

      Je viens de corrigé mon code en conséquence, merci !
      Je me sens un peu con pour le coup, j'ai mal compris la doc à propos des map, je pensais que cette fonctionnalité n'était pas supportée…

  • # s/désigner/designer

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

    Designer et désigner sont deux mots distincts avec des sens complètement différent. Tu peux avantageusement remplacer designer par concevoir, pour lever toute ambiguïté, àmha.

    Sur ce je m’en vais lire le reste du journal qui s’annonce intéressant, merci.

  • # Annyeon, et Map

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

    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 !)

    Tes petits camarades sont donc de Corée du nord, et utilisent donc Kim Jong Ilnux, l'OS codé par l'ancien Grand Leader du pays.

    Problème : cette valeur peu tout à fait être valide dans le cas d'un label situé en début de code !

    Tu pourrais aussi stocker l'adresse d'un label, plus 1. Comme ça, 1 correspond à l'adresse 0, 5 à l'adresse 4, et 0 à file not found.

  • # Abanon

    Posté par  . Évalué à 2.

    J'ai toutefois trouvé un peu bizarre que la gestion du retour à la ligne ne soit pas fournie comme en C++ (avec std::endl).

    Quelqu'un qui maîtrise un peu plus le c++ que moi pour me rectifier en cas d'erreur, mais il me semble que std::endl n'est ni plus ni moins qu'un alias vers "\n". C'est encore dans une couche sous-jacente (le compilateur?) qui fait la conversion vers le système cible.

    • [^] # Re: Abanon

      Posté par  . Évalué à 4.

      Si je ne me trompe pas, std:endl permet de s'affranchir du système de retour à la ligne du système. Par exemple, il peut retourner "\n" ou "\r\n" ou encore autre chose sur un autre système.

      « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

      • [^] # Re: Abanon

        Posté par  . Évalué à 5.

        std::endl flush le buffer au passage. À utiliser avaec parcimonie car cela peut avoir un réel impact sur les performances.

    • [^] # Re: Abanon

      Posté par  . Évalué à 3.

      Pas tout a fait, le std::endl est à la fois
      - un retour à la ligne.
      - un flush (nettoyage) de la sortie utilisée. Soit vider les éventuels buffers internes pour afficher immédiatement (Sur un terminal par exemple).

      • [^] # Re: Abanon

        Posté par  . Évalué à 1.

        Oui, tout à fait, d'ailleurs std::endl est une fonction dont la signature est "ostream& endl(ostream& os);".

  • # Ne pas réinventer la roue

    Posté par  . Évalué à 7.

    • tu peux demander à ton assembleur ou ton compilateur, par exemple la combo GCC/binutils, de sortir un binaire brut ;
    • tu peux dumper ton binaire au format Intel Hex ou Motorola Hex qui peuvent être utilisés directement pour charger les RAM sous ISE (Quartus le fait aussi) : note que pour ModelSim si tu l'utilise il faudra faire le code VHDL pour aller parser le fichier ;

    Mais c'est une bonne excuse pour écrire un assembleur ! ;)

    • [^] # Re: Ne pas réinventer la roue

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

      Tu as même la merveilleuse commande data2mem fournie avec ISE qui sait même convertir des ELF au bon format mémoire qui va bien pour les mémoires de FPGA…

      RTFM !

      Mais j'avoue, bonne excuse pour faire un soft en go !

      Perso, on a utilisé objcopy pour sortir du SREC et un code en C pour réorganiser les données selon le type de mémoire voulu.

  • # Petites questions

    Posté par  . Évalué à 5.

    Est-il possible de ne récupérer qu'une partie des variables retournées (par exemple, uniquement celle qui l'erreur) ?

    Existe-t'il un IDE pour Go (ou un projet d'IDE) ?

    « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

    • [^] # Re: Petites questions

      Posté par  . Évalué à 2.

      Est-il possible de ne récupérer qu'une partie des variables retournées

      Oui, en utilisant la variable spéciale _ . Par exemple, lorsque tu itères un dictionnaire (map) et que tu ne veux utiliser que les valeurs :

      for _, value := range array {
      ...
      }
      
      

      Existe-t'il un IDE pour Go (ou un projet d'IDE) ?

      Oui, il y en a des spécialisés, comme GolangIDE. Il y a également une extension Eclipse, IntelliJ… et des plugins Vim / Emacs (gocode pour l'autocomplétion)… La liste des efforts est concentrée sur Cat-V.

  • # Ca mérite une dépêche

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

    On parle beaucoup de GO, pour une fois que l'on voit du concret.

    ウィズコロナ

    • [^] # Re: Ca mérite une dépêche

      Posté par  . Évalué à 3.

      C'est en cours, il manque un vote.

      « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

      • [^] # Re: Ca mérite une dépêche

        Posté par  . Évalué à 3.

        À quand une fonctionnalité pour passer le journal en dépêche sans perdre le tout plein de comz ? :)

        • [^] # Re: Ca mérite une dépêche

          Posté par  . Évalué à 2.

          Je ne retrouve plus l'entrée de suivi mais on en a déjà discuter longuement, l'ambiance des commentaires des journaux ne sont pas les même que ceux des dépêches, sans compter qu'un journal est parfois modifié quand il passe en dépêche, ce qui rend beaucoup de commentaires obsolètes. De plus, les commentaires ne sont pas perdu, ils sont toujours accessibles dans le journal.

          « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

Suivre le flux des commentaires

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