您的位置:首页 > 编程语言 > Go语言

go语言的cgo简单教程

2017-03-08 15:19 447 查看
目前Go语言有2套编译器:GC和gccgo。其中GC提供的cgo支持C语言,gccgo支持C/C++。

此外,SWIG从2.0.1之后也对go语言提供支持,可以支持C++的类和回调。Go官方提供cmd/go命令,

可以很好的支持cgo,swig支持目前还在完善之中。

本文将简要介绍基于cgo集成C/C++库,适用平台: Linux/Windows。

1. Hello, 世界

// hello.go

package main

import "fmt"

func main() {

fmt.Printf("Hello, 世界\n")

}

由于go编译速度很快,并且cmd/go支持远程get第三方库,go可以方便的作为脚本语言使用。

运行上面的程序, 可以直接输入: go run hello.go。

2. 基于CGO的 Hello, 世界

package main

/*

#include <stdio.h>

*/

import "C"

func main() {

C.puts(C.CString("Hello, 世界\n"))

}

其中 import "C" 前注释中可以导入C语言 函数/变量/宏 等到一个虚拟的 "C" 包中。

我们可以直接使用C.前缀访问C中的函数。

当前的例子中,使用C的puts函数输出"Hello, 世界"。之所以没有使用C语言经典的printf函数,

是因为printf的参数是可变的,而cgo不支持访问你可变参数的函数。

如果需要使用printf函数,需要再做一次包装。

3. 基于C语言printf的 Hello, 世界

package main

/*

#include <stdio.h>

static void myPrint(const char* msg) {

printf("myPrint: %s", msg);

}

*/

import "C"

func main() {

//C.puts(C.CString("Hello, 世界\n"))

//C.printf(C.CString("Hello, 世界\n"))
// error

C.myPrint(C.CString("Hello, 世界\n"))

}

我们通过myPrint来屏蔽printf的可变参数特性。在 import "C" 定义的函数一般建议定义为static。

4. CGO常用的函数

CGO内置了一些常用的函数:

// Go string to C string

// The C string is allocated in the C heap using malloc.

// It is the caller's responsibility to arrange for it to be

// freed, such as by calling C.free.

func C.CString(string) *C.char

// C string to Go string

func C.GoString(*C.char) string

// C string, length to Go string

func C.GoStringN(*C.char, C.int) string

// C pointer, length to Go []byte

func C.GoBytes(unsafe.Pointer, C.int) []byte

我们前面的例子就是使用了 C.CString 将go的字符串转换为C语言的char*类型。

C.CString 返回的空间由C语言的malloc分配,使用完毕后需要用free释放。

5. 释放C.CString分配的空间

package main

/*

#include <stdio.h>

#include <stdlib.h>

*/

import "C"

import "unsafe"

func main() {

cStr := C.CString("Hello, 世界\n")

defer C.free(unsafe.Pointer(cStr))

C.puts(C.CString("Hello, 世界\n"))

}

C.CString返回的是C语言的char*类型,对应go语言的*C.char。 C语言的free参数是void*类型,

对应go语言的unsafe.Pointer。由于go语言禁止2种不同类型的隐式转换,因此用C.free时需要手工转换

类型,对应代码 unsafe.Pointer(cStr)。这样cStr的空间就不会出现内存泄露了。

另外,使用CGO时,一般都会导入"unsafe"包。"unsafe"中提供了一些类似C语言中的底层工具:

func Alignof(v ArbitraryType) uintptr

func Offsetof(v ArbitraryType) uintptr

func Sizeof(v ArbitraryType) uintptr

type ArbitraryType

type Pointer

6. 常见的C/go类型转换

使用CGO时,c和go之间的基本数据类型转换是经常遇到的一个问题。

整数/浮点数 转换:

// c -> go

i_go = int(C.i_c)

f32_fo = float32(C.float_c)

f64_fo = float64(C.double_c)

// go -> c

i_c = C.int(i_go)

float_c = C.float(f32_go)

double_c = C.double(f64_go)

字符串转换:

// c -> go

str_go = C.GoString(C.str_c)

// go -> c

str_c = C.CString(str_go)

指针转换:

// c -> go

p_int_go = (*int)(unsafe.Pointer(C.p_int))

// go -> c

p_int_c = (*C.int)(unsafe.Pointer(&myIntSlice[0]))

比如要调用C语言的main函数(代码不能运行,只是为了说明字符串数组转换):

func CallMain(args []string) int {

argc := C.int(len(args))

argv := make([]*C.char, len(args))

for i := 0; i < len(args); i++ {

argv[i] = C.CString(args[i])

}

rv := C.main(argc, (**C.char)(unsafe.Pointer(&argv)))

return int(rv)

}

如果是回调函数,需要在C中作一层封装(相对比较麻烦)。

7. 连接选项

可以使用#cgo扩展预处理命令指定CFLAGS/LDFLAGS。

比如go-gdal库的连接参数为:

/*

#include "go_gdal.h"

#cgo linux pkg-config: gdal

#cgo darwin pkg-config: gdal

#cgo windows LDFLAGS: -lgdal.dll

*/

import "C"

#cgo之后可以跟系统命令,指定参数对应生效的系统。对于Linux等系统,可以直接使用pkg-config。

8. 集成C++库

目前cgo不识别C++语法。如果需要使用C++库,可以将C++库的API封装为C语言形式,然后编译为动态库。

根据动态库生成对于的导出lib和一个只有C API说明的头文件,然后可以导入go环境使用。

注:

CGO的内容比较杂,而且需要搭建完备的开发环境,现整理一点,以后慢慢补充。

目前,笔者尝试过2个C库的go帮定, 读者可以参考。网址是:

http://code.google.com/p/go-gdal/

http://code.google.com/p/go-opencv/

原文转自:http://chaishushan.blog.163.com/blog/static/1301928972012799345127/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  go cgo