go cgo
Go语言高级编程(Advanced Go Programming)第2章 CGO编程
鸟窝 CGO 文章整理
(...) cgo的指针传递 - 敦刻尔克的小兵 - SegmentFault 思否
https://github.com/chai2010/advanced-go-programming-book/tree/master/ch2-cgo
第二章 CGO编程-Go语言高级编程
chai2010/gopherchina2018-cgo-talk: GopherChina2018: 深入CGO编程 - 最新修订
winlinvip/go-fdkaac: Golang binding for lib-fdkaac(https://github.com/winlinvip/fdk-aac)
全面总结: Golang 调用 C/C++,例子式教程 - 掘金
https://github.com/golang/go/wiki/cgo
https://golang.org/cmd/cgo/
https://blog.golang.org/c-go-cgo
https://book.douban.com/subject/3652388/
https://golang.org/src/runtime/cgocall.go
c++ bindings for go
gocv golang 调用cpp
Examples of calls between Go and C/C++
豆瓣 C++ / #python / #golang #libmc
NVIDIA Management Library (NVML) bindings for Go
https://segmentfault.com/a/1190000017981732
常见问题
CGO构建程序会自动构建当前目录下的C源文件,即是 go 会将当前目录下 .c 文件都编译成 .o目标文件,再链接汇编,这个特点衍生出几个注意事项:
go 代码以静态库或动态库方式引用 C 函数的话,需要将对应的C源文件移出 go源文件所在的目录
如果想要将 C 函数编译到 go 程序,就需要将 C源文件与 go 文件放在同一目录下
在C/C++混编下, go 中引用 C 函数,需要将 C 函数名置于全局,即 extern C
要使用CGO特性,需要安装C/C++构建工具链,在macOS和Linux下是要安装GCC,在windows下是需要安装MinGW工具。同时需要保证环境变量CGO_ENABLED被设置为1,这表示CGO是被启用的状态。在本地构建时CGO_ENABLED默认是启用的,当交叉构建时CGO默认是禁止的。比如要交叉构建ARM环境运行的Go程序,需要手工设置好C/C++交叉构建的工具链,同时开启CGO_ENABLED环境变量。然后通过import "C"语句启用CGO特性。
编译go程序提示undefined reference to xxx ?
解决方法其实在go文件中(配置C的地方),加入参数就行:
添加#cgo LDFLAGS: -lm
同时我还碰到了“undefined reference to `dladdr”错误,😓
解决方案是:
添加:#cgo LDFLAGS:-ldl
C的struct的字段类型 是Go的关键字 ?
struct pcap
{
int type;
char *username;
char *password;
};
然后使用Cgo调用
var a *C.pcap
a.type // 此处会报错,因为type是Go中的关键字
如果C的struct的字段类型是Go的关键字,如 type , 那么在Go代码中可以在字段前加关键字如 x._type
Cgo中使用var声明C结构的变量是否需要释放内存?
func main() {
var pkt_header C.struct_pcap_pkthdr // 声明C struct的变量
var p_header *C.struct_pcap_pkthdr
// 请问是否需要手动释放该变量?求大神解答,谢谢
// defer C.free(unsafe.Pointer(&pkt_header)) ?
// defer C.free(unsafe.Pointer(p_header))
}
pkt_header是在go中分配的,所以不需要手动去释放。
CGO 在GO语言中是桥梁,枢纽,灵魂
unsafe.Pointer 可以让你无视 Go 的类型系统,完成任何类型与内建的 uintptr 类型之间的转化。根据文档,unsafe.Pointer 可以实现四种其他类型不能的操作:
任何类型的指针都可以转化为一个 unsafe.Pointer
一个 unsafe.Pointer 可以转化成任何类型的指针
一个 uintptr 可以转化成一个 unsafe.Pointer
一个 unsafe.Pointer 可以转化成一个 uintptr
GO最重要的概念就是指针: unsafe包中的 unsafe.Pointer
和 builtin中的uintptr
任意类型强转成 unsafe.Pointer
的目的是为了做类型转换
数值类型强转成 uintptr
的目的是为了做指针运算
var p unsafe.Pointer = nil // unsafe 目的:是把一个值变成一个指针 (可以进行指针赋值等操作)
var q uintptr = uintptr(p) // builtin 目的:就是把一个uint的值变成一个可以进行指针运算的值 (可以进行地址加减操作)
看下面例子,
- 把指针变地址
uintptr(unsafe.Pointer(&x)
- 把地址变指针
pb := (*int16)(unsafe.Pointer(uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)))
在C中 对应的就是
void *p = NULL; // unsafe.Pointer === void *p
uintptr_t q = (uintptr_t)(p); // <stdint.h> uintptr_t === uintptr
II. cgo参数传递
将go中的 []string ==> **C.char
var argv =[]string{"../Models/.../gti_gnet3.model", "../Data/Image_lite/bridge_c20.bin"}
C.main(C.int(len(argv)),(**C.char)(unsafe.Pointer(&argv[0]))) //main(int argc,char**argv)
[]byte ==> *C.char
c_char := (*C.char)(unsafe.Pointer(&bt[0]))
[]byte ==> *C.uint8_t
libfacedetection_capi_result_t* libfacedetection_capi_facedetect_rgba(uint8_t* rgba, int width, int height, int step);
func DetectFaceRGB(rgb []byte, w, h, stride int) []Face {
rv := C.libfacedetection_capi_facedetect_rgb(
(*C.uint8_t)(unsafe.Pointer(&rgb[0])),
C.int(w),
C.int(h),
C.int(stride),
)
defer C.libfacedetection_capi_result_free(rv)
}
cgo []byte ==> void * 和void ** 传递方式
在C语言中,用void **
表达指针数组是自然的方式。
int match(void *feat, void **models, int model_num, float *score, int *index);
func (s *VprLib) Predict(feat []byte, model []byte) (float32, error) {
cFeat := unsafe.Pointer(&feat[0]) // void *
cModelNum := C.int(1)
cModels := make([]uintptr, cModelNum)
cModels[0] = uintptr(unsafe.Pointer(&model[0])) // void **
var cScore C.float
var cIndex C.int
ret := C.ul_vpr_match(cFeat,
(*unsafe.Pointer)(unsafe.Pointer(&cModels[0])),
cModelNum,
&cScore,
&cIndex)
return float32(cScore), nil
}
Go语言中开始这么写,是错误的❌
func (s *Lib) Predict(feat []byte, model []byte) (float32, error) {
cFeat := unsafe.Pointer(&feat[0]) // void *
cModelNum := C.int(1)
cModels := make([]unsafe.Pointer, cModelNum) //错误
cModels[0] = unsafe.Pointer(&model[0])
var cScore C.float
var cIndex C.int
ret := C.ul_vpr_match(cFeat,
(*unsafe.Pointer)(unsafe.Pointer(&cModels[0])),
cModelNum,
&cScore,
&cIndex)
return float32(cScore), asError(ret, "match")
}
测试问题:
panic: runtime error: cgo argument has Go pointer to Go pointer [recovered]
helloword.go
package main
// #include <stdlib.h>
import "C"
import (
"fmt"
)
func main() {
fmt.Println(int(C.random()))
}
当然这并不是Hello World。我们不首先输出Hello World是有原因的,接下来就会讲到。不过首先我们分析一下现在这个程序。 首先从结构上来看可以知道这就是一个普通的Go程序,第一行 package main 声明这个代码是在main包里。然后下面有 func main 是程序的入口。
// #inlcude 接下来我们看看Hello World。 首先我们需要三个文件,helloworld.h: helloworld.c: 为啥不直接 C.printf 输出呢,因为在wiki中提到cgo目前暂时还不支持变长参数的C函数,所以要我们自己包装一下。编译: $ go build 也可以我们先把C编译成动态链接库,然后在Go里指示链接: 我们先把 helloworld.c 编译成动态链接库 libhelloworld.so。 C和Go中有很多类型是对应的,但是需要我们自行转换类型。例如: 具体参考:https://golang.org/cmd/cgo/#hdr-Go_references_to_C 此外,对于指针类型,则是该咋用咋用,比如 *C.int。但是对于 void *,则需要用 unsafe.Pointer来表示。 其他知识 CGO是如何运行的 runtime.asmcgocall 会切换到m->go 的栈然后执行代码,因为 g0 的栈是操作系统分配的栈(大小为8k),足够 执行C代码。 _cgo_Cfunc_f 在frame中执行C函数,然后返回到 runtime.asmcgocall。之后再切回调用它的 G的栈。 Cgo可以创建出能够调用C代码的Go包。 使用cgo书写普通的Go代码,导入一个伪包“C”。Go代码就可以关联到如C.size_t的类型,如C.stdout的变量,或者如C.putchar的方法。 如果“C”包的导入紧接在注释之后,那个这个注释,被称为前导码【preamble】,就会作为编译Go包中的C语言部分的头文件。例如: // #include 可以查看$GOROOT/misc/cgo/stdio和$GOROOT/misc/cgo/gmp中的例子。也可以在https://golang.org/doc/articles/c_go_cgo.html 中了解使用cgo的介绍。 CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS,和LDFLAGS可以在注释区域中,使用#cgo伪指令来定义,以改变C,C++,或Fortran编译器的行为。被多个指令定义的值会被联系在一起。指令还可以包含一个构建约束的列表,将其对系统的影响限制为满足其中一个约束条件。可以查看https://golang.org/pkg/go/build/#hdr-Build_Constraints 了解约束语法的详情。例如: 或者,CPPFLAGS和LDFLAGS也可通过pkg-config工具获取,使用#cgo pkg-config:指令,后面跟上包名即可。比如: 默认的pkg-config工具可以通过设置PKG_CONFIG环境变量来改变。 当编译时,CGO_CFLAGS,CGO_CPPFLAGS,CGO_CXXFLAGS,CGO_FFLAGS和CGO_LDFLAGS这些环境变量都会从指令中提取出来,并加入到flags中。包特定的flags需要使用指令来设置,而不是通过环境变量,所以这些构建可以在未更改的环境中也能正常运行。 一个包中的所有cgo CPPFLAGS和CFLAGS指令会被连接起来,并用来编译包中的C文件。一个包中的所有CPPFLAGS和CXXFLAGS指令会被连接起来,并用来编译包中的C++文件。一个包中的所有CPPFLAGS和FFLAGS指令会被连接起来,并用来编译包中的Fortran文件。在这个程序中任何包内的所有LDFLAGS指令会被连接起来,并在链接时使用。所有的pkg-config指令会被连接起来,并同时发送给pkg-config,以添加到每个适当的命令行标志集中。 当cgo指令被转化【parse】时,任何出现${SRCDIR}字符串的地方,都会被替换为包含源文件的目录的绝对路径。这就允许预编译的静态库包含在包目录中,并能够正确的链接。例如,如果foo包在/go/src/foo目录下: 当Go tool发现一个或多个Go文件使用了特殊导入“C”包,它就会在当前目录中寻找其它非Go文件,并将其编译为Go包的一部分。 cgo tool对于本地构建默认是启用的。而在交叉编译时默认是禁用的。你可以在运行go tool时,通过设置CGO_ENABLED环境变量来控制它:设为1表示启用cgo, 设为0关闭它。如果cgo启用,go tool将会设置构建约束“cgo”。 在交叉编译时,你必须为cgo指定一个C语言交叉编译器。你可以在使用make.bash构建工具链时,设置 在Go文件中,C的结构体属性名是Go的关键字,可以加上下划线前缀来访问它们:如果x指向一个拥有属性名"type"的C结构体,那么就可以用x._type来访问这个属性。对于那些不能在Go中表达的C结构体字段(例如位字段或者未对齐的数据),会在Go结构体中被省略,而替换为适当的填充,以到达下一个属性,或者结构体的结尾。 标准C数字类型,可以通过如下名字访问: 心得:但是如果C结构体用了typedef struct设置了别名,则就不需要加上前缀,可以直接C.xxx_t 访问该类型。 因为在通常情况下Go并不支持C的联合体类型【union type】,所以C的联合体类型,由一个等长的Go byte数组来表示。 Go结构体不能嵌入具有C类型的字段。 Go代码不能引用发生在非空C结构体末尾的零尺寸字段。如果要获取这个字段的地址(这也是对于零大小字段唯一能做的操作),你必须传入结构体的地址,并加上结构体的大小,才能算出这个零大小字段的地址。 cgo会将C类型转换为对应的,非导出的的Go类型。因为转换是非导出的,一个Go包就不应该在它的导出API中暴露C的类型:在一个Go包中使用的C类型,不同于在其它包中使用的同样C类型。 可以在多个赋值语境中,调用任何C函数(甚至是void函数),来获取返回值(如果有的话),以及C errno变量作为Go error(如果方法返回void,则使用 _ 来跳过返回值)。例如: n, err = C.sqrt(-1) 调用C的方法指针目前还不支持,然而你可以声明Go变量来引用C的方法指针,然后在Go和C之间来回传递它。C代码可以调用来自Go的方法指针。例如: 在C中,一个方法参数被写为一个固定大小的数组,实际上需要的是一个指向数组第一个元素的指针。C编译器很清楚这个调用习惯,并相应的调整这个调用,但是Go不能这样做。在Go中,你必须显式的传入指向第一个元素的指针:C.f(&C.x[0])。 在Go和C类型之间,通过拷贝数据,还有一些特殊的方法转换。用Go伪代码定义如下: 作为一个特殊例子,C.malloc并不是直接调用C的库函数malloc,而是调用一个Go的帮助函数【helper function】,该函数包装了C的库函数malloc,并且保证不会返回nil。如果C的malloc指示内存溢出,这个帮助函数会崩溃掉程序,就像Go自己运行时内存溢出一样。因为C.malloc从不失败,所以它不会返回包含errno的2值格式。 参考: 官方文档: http://blog.golang.org/2011/03/c-go-cgo.html 一份博文,编译过程讲得比较细: http://googollee.blog.163.com/blog/static/1159411201031812128593/ Go语言教程:使用C语言函数: http://chaishushan.blog.163.com/blog/static/130192897201012710273283/ 看完上面的教程,基本上知道怎么用Go调用C代码、和需要注意的事项。 至于C调用Go的代码,Go调用汇编代码,以后再研究吧。 以下内容是笔记,列出一些重点等,不解释。 源文件 注意事项: 不支持调用像 Printf() (待完善) http://my.oschina.net/zengsai/blog/5138 http://my.oschina.net/zengsai/blog/5139 在很多场景下,在 Go 的程序中需要调用 c 函数或者是用 c 编写的库(底层驱动,算法等,不想用 Go 语言再去造一遍轮子,复用现有的 c 库)。 1)先从最简单的写起吧,Go 代码直接调用 c 函数,下面的示例中在代码注释块调用了标准的 c 库,并写了一个 c 函数 (本例只是简单打印了一句话,在该注释块中可以写任意合法的 c 代码),在 Go 代码部分直接调用该 c 函数 运行结果: 好,我可以在 Go 代码中写 c 代码了,那么我该如何在 Go 中直接调用已经编译好的第三方 c 库呢?用 h 文件 编译成动态库. so Go 文件 重点来了(敲黑板): 更深入一些,
import "C"
这三行是Go调C才这样的,import "C" 是为了可以在Go程序里直接使用C里的一些函数,例如main中 C.random(),而 import "C" 上边的注释叫做preamble,注意必须和 import "C"紧紧挨着中间不能有空格。注释的风格可以是 // #include... 也可以是 /#include .../ 这样的。此外可以在 preamble 中加入 // #cgo 开头的注释,用于指示编译和链接中发生的一些事情,例如链接哪个动态链接库等。#ifndef libfacedetection_capi_h_
#define libfacedetection_capi_h_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void Printf(char *s);
#ifdef __cplusplus
}
#endif
#endif // libfacedetection_capi_h_
#include <stdio.h>
void Printf(char *s) {
printf("%s", s);
}
main.go:
package main
// #include "helloworld.h"
import "C"
func main() {
C.Printf("hello world")
}
./main.go:7: cannot use "hello world" (type string) as type *_Ctype_char in argument to _Cfunc_Printf
原因是C和Go的字符串不是通用的,我们要把Go的字符串转成C的字符串,但是因为不是在编译的这个过程申请内存,而是在堆里 申请内存存储字符串,而Go的垃圾回收是管不到C申请的内存,所以我们需要自行销毁对应的内存。package main
// #include <stdlib.h>
// #include "helloworld.h"
import "C"
import (
"unsafe"
)
func main() {
cs := C.CString("hello world\n")
defer C.free(unsafe.Pointer(cs))
C.Printf(cs)
}
g++ -fPIC -shared -o libhelloword.so *.cpp
package main
// #cgo LDFLAGS: -L${SRCDIR}/ -Wl,-rpath,${SRCDIR}/ -lhelloworld
// #include <stdlib.h>
// #include "helloworld.h"
import "C"
import (
"unsafe"
)
func main() {
cs := C.CString("hello world\n")
defer C.free(unsafe.Pointer(cs))
C.Printf(cs)
}
CGO类型
如果是访问struct得这么用 C.struct_
如果说需要什么其他知识,那就是编译链接相关的知识了,推荐《程序员的自我修养-链接、装载与库》。这本书非常的好,国产神书, 不过说实话,因为不经常接触,看过然后又忘记了大部分内容🤦♂️
在Go中调用C函数,cgo生成的代码调用 runtime.cgocall(_cgo_Cfunc_f, frame),_cgo_Cfunc_f 就是GCC编译 出来的代码。 runtime.cgocall 会调用 runtime.asmcgocall(_cgo_Cfunc_f, frame)。在go命令行中使用cgo
// #include
import "C"
前导码【preamble】可以包含任何C语言代码,包括方法和变量声明及定义。这些代码后续可能会由Go代码引用,就像它们定义在了名为“C”的Go包中。所有在前导码中声明的名字都可能被使用,即使它们以小写字母开头。有一个例外:前导码中的static变量不能被Go代码引用;而static方法则可以被Go引用。// #cgo CFLAGS: -DPNG_DEBUG=1
// #cgo amd64 386 CFLAGS: -DX86=1
// #cgo LDFLAGS: -lpng
// #include <png.h>
import "C"
// #cgo pkg-config: png cairo
// #include <png.h>
import "C"
// #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo
就会被展开为:// #cgo LDFLAGS: -L/go/src/foo/libs -lfoo
心得: // #cgo LDFLAGS: 可用来链接静态库。-L指定静态库所在目录,-l指定静态库文件名,注意静态库文件名必须有lib前缀,但是这里不需要写,比如上面的-lfoo实际找的是libfoo.a文件
任何.c, .s, 或者.S文件会被C编译器编译。
任何.cc, .cpp, 或者.cxx文件会被C++编译器编译。
任何.f, .F, .for或者.f90文件会被fortran编译器编译。
任何.h, .hh, .hpp或者.hxx文件不会被分开编译,但是,如果这些头文件修改了,那么C和C++文件就会被重新编译。默认的C和C++编译器有可能会分别被CC和CXX环境变量改变,那些环境变量可能包含命令行选项。CC_FOR_TARGET
环境变量来指定,或者是在你运行go tool时设置CC环境变量来指定。与此相似的还有作用于C++代码的CXX_FOR_TARGET
和CXX环境变量。Go引用C代码
_, err := C.voidFunc()
var n, err = C.sqrt(1)package main
/*
typedef int (*intFunc) ();
int bridge_int_func(intFunc f){
return f();
}
int fortytwo(){
return 42;
}
*/
import "C"
import "fmt"
func main() {
f := C.intFunc(C.fortytwo)
fmt.Println(int(C.bridge_int_func(f)))
// Output: 42
}
Go调用C代码,Cgo笔记
/*
// 这是注释中的注释
#include <stdio.h>
#include <errno.h>
*/
import "C"
// #cgo CFLAGS: -DPNG_DEBUG=1
// #cgo linux CFLAGS: -DLINUX=1
// #cgo LDFLAGS: -lpng
// #include <png.h>
import "C"
// #cgo pkg-config: png cairo
// #include <png.h>
import "C"
···
编译方法
include $(GOROOT)/src/Make.inc
TARG=xdb
CGOFILES=xdb.go
CGO_CFLAGS+=-L/opt/xunsearch/lib/ -I/opt/xunsearch/include
CGO_LDFLAGS+=-lscws -L/opt/xunsearch/lib/
include $(GOROOT)/src/Make.pkg
说明:CFlags等参数,可以写在Go的源代码中
#### 数据类型转换
C -> Go
int(C.int )
// 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
Go -> C
C.char
C.schar (signed char)
C.uchar (unsigned char)
C.short
C.ushort (unsigned short)
C.int
C.uint (unsigned int)
C.long
C.ulong (unsigned long)
C.longlong (long long)
C.ulonglong (unsigned long long)
C.float
C.double
unsafe.Pointer (void*)
// Go string to C string
func C.CString(string) *C.char
var val []byte
(*C.char)(unsafe.Pointer(&val[0]))
内存释放
cs := C.CString(s)
defer C.free(unsafe.Pointer(cs))
Go创建的对象,可以很好的回收;而C创建的,则需要手动回收
不清楚的地方,问人,或者看别人用Go调用C的代码。
http://googollee.blog.163.com/blog/static/1159411201031812128593/package main
// First, build Skia this way:
// ./gyp_skia -Dskia_shared_lib=1 && ninja -C out/Debug
/*
#cgo LDFLAGS: -lm
#cgo LDFLAGS: -lpng
#cgo LDFLAGS: -lstdc++
#cgo LDFLAGS: -L ../../out/Debug/lib
#cgo LDFLAGS: -Wl,-rpath=../../out/Debug/lib
#cgo LDFLAGS: -lskia
#cgo CFLAGS: -I../../include/c
#include "sk_surface.h"
*/
import "C"
import (
"fmt"
)
func main() {
p := C.sk_paint_new()
defer C.sk_paint_delete(p)
fmt.Println("OK!")
}
// TODO: replace this with an idiomatic interface to Skia.
那么该如何调用呢?Go 可是更好的 C 语言啊,当然提供了和 c 语言交互的功能,称为Cgo
!Cgo
封装了#cgo
伪 c 文法,参数CFLAGS
用来传入编译选项,LDFLAGS
来传入链接选项。这个用来调用非 c 标准的第三方 c 库。hi()
package main
import "fmt"
/*
#include <stdio.h>
void hi() {
printf("hello world!\n");
}
*/
import "C" //这里可看作封装的伪包C, 这条语句要紧挨着上面的注释块,不可在它俩之间间隔空行!
func main() {
C.hi()
fmt.Println("Hi, vim-go")
}
root@slave2:/home/cgo# go run main.go
hello world!
Hi, vim-go
Cgo
!
2)本例示范在 Go 代码中调用非标准的 c 的第三方动态库
c 文件/*
* hi.c
* created on: July 1, 2017
* author: mark
*/
#include <stdio.h>
void hi() {
printf("Hello Cgo!\n");
}
void hi();
root@slave2:/home/cgo# gcc -c -fPIC -o hi.o hi.c
root@slave2:/home/cgo# gcc -shared -o libhi.so hi.o
package main
import "fmt"
/*
#cgo CFLAGS: -I./
#cgo LDFLAGS: -L./ -lhi
#include "hi.h" //非标准c头文件,所以用引号
*/
import "C"
func main() {
C.hi()
fmt.Println("Hi, vim-go")
}
CFLAGS
中的-I
(大写的 i) 参数表示.h
头文件所在的路径LDFLAGS
中的-L
(大写) 表示. so 文件所在的路径 -l
(小写的 L) 表示指定该路径下的库名称,比如要使用libhi.so
,则只需用-lhi
(省略了libhi.so
中的lib
和.so
字符,关于这些字符所代表的具体含义请自行 google)表示。
运行结果:root@slave2:/home/cgo# go run main.go
Hello Cgo!
Hi, vim-go
1)头文件路径和库文件路径写死的话,一旦第三方库的安装路径变化了,Golang 的代码也要跟着变化,就会很麻烦。这时可以使用cgo
命令中使用pk-config
,具体请参考这篇博文:Golang 使用 pkg-config 自动获取头文件和链接库的方法
2)当在 Go 中使用了以上的方法后,就要求主机(或者云服务器)上必须有相应的.so
文件,如果不存在就会链接报错,导致程序退出。
若.so
是一些不必要的第三方驱动库(可有可无),那就麻烦了,你不能为了跑这个程序,把每台主机都装上那个不必要的第三方库吧。有没有一种方法可以在 Go 程序运行时才调用这些.so
库呢,如果不存在忽略就好(就不启用那个库提供的功能了,而不是链接报错直接异常退出)?当然有!敬请期待一下一篇。:)// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains tests for the cgo checker.
package a
// void f(void *ptr) {}
import "C"
import "unsafe"
func CgoTests() {
var c chan bool
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&c))) // want "embedded pointer"
C.f(unsafe.Pointer(&c)) // want "embedded pointer"
var m map[string]string
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&m))) // want "embedded pointer"
C.f(unsafe.Pointer(&m)) // want "embedded pointer"
var f func()
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&f))) // want "embedded pointer"
C.f(unsafe.Pointer(&f)) // want "embedded pointer"
var s []int
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&s))) // want "embedded pointer"
C.f(unsafe.Pointer(&s)) // want "embedded pointer"
var a [1][]int
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&a))) // want "embedded pointer"
C.f(unsafe.Pointer(&a)) // want "embedded pointer"
var st struct{ f []int }
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st))) // want "embedded pointer"
C.f(unsafe.Pointer(&st)) // want "embedded pointer"
var st3 S
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st3))) // want "embedded pointer"
C.f(unsafe.Pointer(&st3)) // want "embedded pointer"
// The following cases are OK.
var i int
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&i)))
C.f(unsafe.Pointer(&i))
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&s[0])))
C.f(unsafe.Pointer(&s[0]))
var a2 [1]int
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&a2)))
C.f(unsafe.Pointer(&a2))
var st2 struct{ i int }
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st2)))
C.f(unsafe.Pointer(&st2))
var st4 S2
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st4)))
C.f(unsafe.Pointer(&st4))
type cgoStruct struct{ p *cgoStruct }
C.f(unsafe.Pointer(&cgoStruct{}))
C.CBytes([]byte("hello"))
}
type S struct{ slice []int }
type S2 struct{ int int }