Cgo:第一步教程

一些例子來理解使用 Go C Bindings 的工作流程

什麼

在 Go 中,你可以使用 cgo 呼叫 C 程式和函式。這樣,你可以輕鬆地為提供 C API 的其他應用程式或庫建立 C 繫結。

怎麼樣

所有你需要做的是增加你的圍棋程式的開始 import "C" 包括你的 C 程式後:

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

在前面的示例中,你可以在 Go 中使用 stdio 包。

如果你需要使用同一資料夾中的應用程式,則使用與 C 相同的語法(使用 " 而不是 <>

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

重要提示 :不要includeimport "C" 語句**之間留下換行符,**否則你將在構建時遇到此類錯誤:

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

這個例子

在此資料夾中,你可以找到 C 繫結的示例。我們有兩個非常簡單的 C叫做 hello.c

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

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

這只是在控制檯和 sum.c 中列印 hello world

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

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

…需要 2 個引數並返回其總和(不要列印它)。

我們有一個 main.go 程式,它將使用這兩個檔案。首先我們如前所述匯入它們:

//main.go
package main

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

Hello World!

現在我們準備在 Go 應用程式中使用 C 程式了。我們先試試 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
}

現在使用 go run main.go 執行 main.go 程式來獲取 C 程式的列印:Hello World!。做得好!

整數總和

讓我們通過新增一個對其兩個引數求和的函式來使它更復雜一些。

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

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

我們將從之前的 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
", 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
}

看看 makeSum 功能。它通過使用 C.int 函式接收兩個需要轉換為 C intint 引數。此外,呼叫的返回將給我們一個 C int 並在出現問題時出錯。我們需要使用 int() 將 Go 響應轉換為 Go 的 int。

嘗試使用 go run main.go 執行我們的應用程式

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

生成二進位制檔案

如果你嘗試 go build,你可能會遇到多個定義錯誤。

$ 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

訣竅是在使用 go build 時直接引用主檔案:

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

請記住,你可以使用 -o flag go build -o my_c_binding main.go 為二進位制檔案提供名稱

我希望你喜歡這個教程。