go語言的cgo簡單教程
來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。以下內容轉載自:http://chaishushan.blog.163.com/blog/static/1301928972012799345127/
作者: chai2010 <chaishushan{AT}gmail.com>著作權: 轉載請聲明原作者
目前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.gopackage 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"))// errorC.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 stringfunc C.GoString(*C.char) string
// C string, length to Go stringfunc C.GoStringN(*C.char, C.int) string
// C pointer, length to Go []bytefunc 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) uintptrfunc Offsetof(v ArbitraryType) uintptrfunc Sizeof(v ArbitraryType) uintptrtype ArbitraryTypetype Pointer
6. 常見的C/go類型轉換
使用CGO時,c和go之間的基礎資料型別 (Elementary Data Type)轉換是經常遇到的一個問題。
整數/浮點數 轉換:
// c -> goi_go = int(C.i_c)f32_fo = float32(C.float_c)f64_fo = float64(C.double_c)
// go -> ci_c = C.int(i_go)float_c = C.float(f32_go)double_c = C.double(f64_go)
字串轉換:
// c -> gostr_go = C.GoString(C.str_c)
// go -> cstr_c = C.CString(str_go)
指標轉換:// c -> gop_int_go = (*int)(unsafe.Pointer(C.p_int))// go -> cp_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/