這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
CGO
CGO可以讓golang和c互相調用,譬如x264或者aac編解碼,用go再實現一遍比較麻煩,直接調用c的函數會更合適。
CGO可以直接用C的代碼,或者C的靜態庫,或者動態庫,當然C++也是可以的。
寫了一個fdkaac的binding:https://github.com/winlinvip/go-fdkaac
參考這兩篇文章,講得很清楚:
https://golang.org/cmd/cgo/
https://blog.golang.org/c-go-cgo
有個例子,go調用x264的函數:https://github.com/winlinvip/codec
在import “C”之前加preamble(注釋),包含標頭檔。這樣在C這個命名空間中就可以用C的函數了。
這個C包,實際上是個偽包,匯入後會解析前面的preamble,用到標頭檔定義的類型、變數和函數。
preamble中可以有include,代碼,宏定義,編譯條件;靜態變數不可用,靜態函數可用。
編譯條件是用#cgo指定的,包括CFLAGS、CPPFLAGS、CXXFLAGS、LDFLAGS。
cgo指令還可以用一些變數,譬如${SRCDIR}用來連結靜態庫。
下面是個C++匯出的庫:
// winlin.h#ifdef __cplusplusextern "C"{#endif// get the version.extern int winlin_version();#ifdef __cplusplus}#endif
// winlin.cpp// g++ -c -o winlin.o winlin.cpp && ar -rs winlin.a winlin.o#include "winlin.h"int winlin_version() { return 0x01020304;}
// main.cpp// g++ -o cpp main.cpp winlin.a#include <stdio.h>#include "winlin.h"int main(int argc, char** argv) { printf("version is %#x\n", winlin_version()); return 0;}
下面是main.cpp在Centos6的執行結果:
[winlin@centos6 gos]$ ./cppversion is 0x1020304[winlin@centos6 gos]$ ldd cpp linux-vdso.so.1 => (0x00007fff9f674000) libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x0000003cbda00000) libm.so.6 => /lib64/libm.so.6 (0x0000003cb0a00000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003cbca00000) libc.so.6 => /lib64/libc.so.6 (0x0000003cb0e00000) /lib64/ld-linux-x86-64.so.2 (0x0000003cb0600000)
cgo,可以在golang中用這個靜態庫:
// go run main.gopackage main// #include "winlin.h"// #cgo LDFLAGS: ${SRCDIR}/winlin.a -lstdc++import "C"import "fmt"func main() { fmt.Println("version is", C.winlin_version())}
注意,得在h標頭檔中,使用c的方式匯出符號。
除此之外,使用c++的庫,得在LDFLAGS中加入-lstdc++。
總之,cgo的編譯和一般的c或c++差別不大,引入的庫和標頭檔之類。
運行結果如下:
[winlin@centos6 gos]$ go build -o test main.go[winlin@centos6 gos]$ ldd test linux-vdso.so.1 => (0x00007fff8df9f000) libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x0000003cbda00000) libpthread.so.0 => /lib64/libpthread.so.0 (0x0000003cb1600000) libc.so.6 => /lib64/libc.so.6 (0x0000003cb0e00000) libm.so.6 => /lib64/libm.so.6 (0x0000003cb0a00000) /lib64/ld-linux-x86-64.so.2 (0x0000003cb0600000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003cbca00000)[winlin@centos6 gos]$ ./testversion is 16909060
注意:preabmle可以是//,或者是//,但是不能是//,不能多個,會解析失敗。
在Centos6下面的golang程式,沒有用cgo:
[winlin@centos6 srs-plus]$ ldd objs/bocar linux-vdso.so.1 => (0x00007fffb1fff000) libpthread.so.0 => /lib64/libpthread.so.0 (0x0000003cb1600000) libc.so.6 => /lib64/libc.so.6 (0x0000003cb0e00000) /lib64/ld-linux-x86-64.so.2 (0x0000003cb0600000)
下面是用了cgo的golang程式:
[winlin@centos6 gos]$ ldd test linux-vdso.so.1 => (0x00007ffff43ff000) libpthread.so.0 => /lib64/libpthread.so.0 (0x0000003cb1600000) libc.so.6 => /lib64/libc.so.6 (0x0000003cb0e00000) /lib64/ld-linux-x86-64.so.2 (0x0000003cb0600000)
下面是C++程式:
[winlin@centos6 srs-plus]$ ldd objs/srs linux-vdso.so.1 => (0x00007fff5d9ff000) libdl.so.2 => /lib64/libdl.so.2 (0x0000003cb1200000) libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x0000003cbda00000) libm.so.6 => /lib64/libm.so.6 (0x0000003cb0a00000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003cbca00000) libc.so.6 => /lib64/libc.so.6 (0x0000003cb0e00000) /lib64/ld-linux-x86-64.so.2 (0x0000003cb0600000)
都只是引用了系統的so,譬如c和c++,pthread還有ldl之類的。