Go Cgo: tutoriel Premiers pas


Exemple

Quelques exemples pour comprendre le flux de travail de l'utilisation des liaisons Go C

Quelle

Dans Go, vous pouvez appeler les programmes et fonctions C en utilisant cgo . De cette façon, vous pouvez facilement créer des liaisons C vers d'autres applications ou bibliothèques fournissant une API C.

Comment

Tout ce que vous avez à faire est d'ajouter un import "C" au début de votre programme Go juste après avoir inclus votre programme C:

//#include <stdio.h>
import "C"

Avec l'exemple précédent, vous pouvez utiliser le package stdio dans Go.

Si vous avez besoin d'utiliser une application qui se trouve dans votre même dossier, vous utilisez la même syntaxe que dans C (avec le " au lieu de <> )

//#include "hello.c"
import "C"

IMPORTANT : Ne laissez pas de nouvelle ligne entre les instructions import "C" include et import "C" ou vous obtiendrez ce type d'erreurs lors de la génération:

# command-line-arguments
could not determine kind of name for C.Hello
could not determine kind of name for C.sum

L'exemple

Dans ce dossier, vous pouvez trouver un exemple de liaisons C. Nous avons deux bibliothèques "C" très simples appelées hello.c :

//hello.c
#include <stdio.h>

void Hello(){
    printf("Hello world\n");
}

Cela imprime simplement "hello world" dans la console et sum.c

//sum.c
#include <stdio.h>

int sum(int a, int b) {
    return a + b;
}

... qui prend 2 arguments et retourne sa somme (ne pas l'imprimer).

Nous avons un programme main.go qui utilisera ces deux fichiers. Nous les importons d’abord comme nous l’avons déjà mentionné:

//main.go
package main

/*
  #include "hello.c"
  #include "sum.c"
*/
import "C"

Bonjour le monde!

Nous sommes maintenant prêts à utiliser les programmes C dans notre application Go. Essayons d'abord le programme Hello:

//main.go
package main

/*
  #include "hello.c"
  #include "sum.c"
*/
import "C"


func main() {
    //Call to void function without params
    err := Hello()
    if err != nil {
        log.Fatal(err)
    }
}

//Hello is a C binding to the Hello World "C" program. As a Go user you could
//use now the Hello function transparently without knowing that it is calling
//a C function
func Hello() error {
    _, err := C.Hello()    //We ignore first result as it is a void function
    if err != nil {
        return errors.New("error calling Hello function: " + err.Error())
    }

    return nil
}

Maintenant, exécutez le programme main.go en utilisant go run main.go pour obtenir l'impression du programme C: "Hello world!". Bien joué!

Somme des ints

Rendons-le un peu plus complexe en ajoutant une fonction qui résume ses deux arguments.

//sum.c
#include <stdio.h>

int sum(int a, int b) {
  return a + b;
}

Et nous l'appellerons depuis notre précédente application Go.

//main.go
package main

/*
#include "hello.c"
#include "sum.c"
*/
import "C"

import (
    "errors"
    "fmt"
    "log"
)

func main() {
    //Call to void function without params
    err := Hello()
    if err != nil {
        log.Fatal(err)
    }

    //Call to int function with two params
    res, err := makeSum(5, 4)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Sum of 5 + 4 is %d\n", res)
}

//Hello is a C binding to the Hello World "C" program. As a Go user you could
//use now the Hello function transparently without knowing that is calling a C
//function
func Hello() error {
    _, err := C.Hello() //We ignore first result as it is a void function
    if err != nil {
        return errors.New("error calling Hello function: " + err.Error())
    }

    return nil
}

//makeSum also is a C binding to make a sum. As before it returns a result and
//an error. Look that we had to pass the Int values to C.int values before using
//the function and cast the result back to a Go int value
func makeSum(a, b int) (int, error) {
    //Convert Go ints to C ints
    aC := C.int(a)
    bC := C.int(b)

    sum, err := C.sum(aC, bC)
    if err != nil {
        return 0, errors.New("error calling Sum function: " + err.Error())
    }

    //Convert C.int result to Go int
    res := int(sum)

    return res, nil
}

Regardez la fonction "makeSum". Il reçoit deux paramètres int qui doivent être convertis en C int avant en utilisant la fonction C.int . De plus, le retour de l'appel nous donnera un C int et une erreur en cas de problème. Nous devons convertir la réponse C en un int de Go en utilisant int() .

Essayez de lancer notre application go en utilisant go run main.go

$ go run main.go
Hello world!
Sum of 5 + 4 is 9

Générer un binaire

Si vous essayez une construction, vous pourriez avoir plusieurs erreurs de définition.

$ go build
# github.com/sayden/c-bindings
/tmp/go-build329491076/github.com/sayden/c-bindings/_obj/hello.o: In function `Hello':
../../go/src/github.com/sayden/c-bindings/hello.c:5: multiple definition of `Hello'
/tmp/go-build329491076/github.com/sayden/c-bindings/_obj/main.cgo2.o:/home/mariocaster/go/src/github.com/sayden/c-bindings/hello.c:5: first defined here
/tmp/go-build329491076/github.com/sayden/c-bindings/_obj/sum.o: In function `sum':
../../go/src/github.com/sayden/c-bindings/sum.c:5: multiple definition of `sum`
/tmp/go-build329491076/github.com/sayden/c-bindings/_obj/main.cgo2.o:/home/mariocaster/go/src/github.com/sayden/c-bindings/sum.c:5: first defined here
collect2: error: ld returned 1 exit status

L'astuce consiste à se référer directement au fichier principal lors de l'utilisation de go build :

$ go build main.go
$ ./main
Hello world!
Sum of 5 + 4 is 9

Rappelez-vous que vous pouvez donner un nom au fichier binaire en utilisant -o flag go build -o my_c_binding main.go

J'espère que vous avez apprécié ce tutoriel.