Golang 和 C 之间相互调用入门。

原文链接:https://github.com/golang/go/wiki/cgo

介绍

首先,http://golang.org/cmd/cgo 是最基础的 cgo 的文档。 关于 cgo 更加详尽的文档在 https://blog.golang.org/c-go-cgo

入门

如果一个 Go 的源码文件引用了 "C" 这个包,那么这就是在用 cgo 了。在整个的编译过程中 Go 将会立即处理位于 import "C" 上边的注释的部分,并且将会和其他的 cgo 的注释 link 到一起,也包括其他所有的 C 文件。

需要注意的是在 import "C" 和 cgo 注释中间不能有空行。

想要获取 C 中的变量只需要用到包名为 C 的 Go 内置的依赖。也就是说,如果你想调用 C 的函数 printf() 在 Go 的代码中,可以这样写 C.printf()。由于 cgo 还不支持可变参数(见issue 975),我们需要将它包裹成另外我们自己的函数 myprint()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package cgoexample

/*
#include <stdio.h>
#include <stdlib.h>

void myprint(char* s) {
    printf("%s\n", s);
}
*/
import "C"

import "unsafe"

func Example() {
    cs := C.CString("Hello from stdio\n")
    C.myprint(cs)
    C.free(unsafe.Pointer(cs))
}

在 C 中调用 Go 方法

利用 cgo 在 C 中执行 Go 代码中定义的全局函数或者函数变量都是可行的。

全局函数

Go 使它的函数在 C 中可见是用的一个特殊的注释 //export。注意:如果你在用 exports,那么你就不能定义任何 C 的方法在文件头的位置。

举例说明,有两个文件 foo.c 和 foo.go

foo.go 的内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package gocallback

import "fmt"

/*
#include <stdio.h>
extern void ACFunction();
*/
import "C"

//export AGoFunction
func AGoFunction() {
    fmt.Println("AGoFunction()")
}

func Example() {
    C.ACFunction()
}

foo.c 的内容:

1
2
3
4
5
#include "_cgo_export.h"
void ACFunction() {
    printf("ACFunction()\n");
    AGoFunction();
} 

函数变量

下边的代码展示了一个 C 中执行 Go 代码中定义的回调函数。由于指针传递规则,Go 代码不能直接传递一个函数变量到 C 中。我们需要用间接的方式来实现。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package gocallback

import (
    "fmt"
    "sync"
)

/*
extern void go_callback_int(int foo, int p1);

// normally you will have to define function or variables
// in another separate C file to avoid the multiple definition
// errors, however, using "static inline" is a nice workaround
// for simple functions like this one.
static inline void CallMyFunction(int foo) {
    go_callback_int(foo, 5);
}
*/
import "C"

//export go_callback_int
func go_callback_int(foo C.int, p1 C.int) {
    fn := lookup(int(foo))
    fn(p1)
}

func MyCallback(x C.int) {
    fmt.Println("callback with", x)
}

func Example() {
    i := register(MyCallback)
    C.CallMyFunction(C.int(i))
    unregister(i)
}

var mu sync.Mutex
var index int
var fns = make(map[int]func(C.int))

func register(fn func(C.int)) int {
    mu.Lock()
    defer mu.Unlock()
    index++
    for fns[index] != nil {
        index++
    }
    fns[index] = fn
    return index
}

func lookup(i int) func(C.int) {
    mu.Lock()
    defer mu.Unlock()
    return fns[i]
}

func unregister(i int) {
    mu.Lock()
    defer mu.Unlock()
    delete(fns, i)
}

The following code shows an example of invoking a Go callback from C code. Because of the pointer passing rules Go code can not pass a function value directly to C. Instead it is necessary to use an indirection. This example uses a registry with a mutex, but there are many other ways to map from a value that can be passed to C to a Go function.