cgo 从 C 传递 slice 到 go
这里一般会用在c的callback中。
需要加一个wrapper,比直接调用go函数中间多了一个转换步骤,但方便了许多。
执行流程为,c调用发起 -> c wrapper -> go export
.go:
//export a_function_callback
func a_function_callback(args []C.astruct) {}
.c:
extern void a_function_callback(GoSlice args);
void a_function_callback_c(args *C.astruct) {
GoSlice args_go;
args_go.data = args;
args_go.len = nargs;
args_go.cap = nargs;
return a_function_callback(args_go);
}
这里的GoSlice需要从 export 出来的 .h 中获取,或者 include 该 .h文件。
也可以直接拷贝过来,注意不要产生类型冲突。
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef long long GoInt64;
typedef GoInt64 GoInt;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
C.CString 的自动化回收方法
一般使用 C.CString 时,定义一个临时变量存储结果,使用 defer C.free 回收这个临时的变量。
在 go 的 runtime 包中,有一个SetFinalizer方法,可以在回收对象的时候调用一个处理函数。
那么我们可以考虑使用它来自动化回收 C.CString产生的临时变量。
type CString struct {
Ptr *C.char
}
func CCString(s string) *CString {
p := C.CString(s)
this := &CString{Ptr: p}
runtime.SetFinalizer(this, freeCString)
return this
}
func freeCString(cs *CString) {
C.free(unsafe.Pointer(cs.Ptr))
}
在使用的时候,基本能嵌入到调用的位置,不需要显式定义一个临时变量并执行 defer C.free。
C.somefunc(CCString(s).Ptr)
注意,只在somefunc不接管传递的参数的所有权时调用这种方式,如果somefunc接管了所有权,
而在未来某时间内存被回收了,则会出现程序SEGFAULT崩溃。
这种可能会产生一点runtime效率损失,另外还有额外的内存分配,看情况使用。
bool 类型的转换处理
在 C 语言中,c99之前 bool 都是定义为 int的,要想转换为go的bool类型,比较曲折。
var bval_c C.GBoolean
var bval_go bool
if int(bval) == 1 {
bval_go = true
} else {
bval_go = false
}
如果go有三元运算符的话,也可一行代码实现该转换,问题是go没有三元运算符。
一般来说,对其包装一个简单的转换函数比较好,比如 c2gobool(C.GBoolean).
有时候不同的 C 项目中 bool 定义不同,所以一般需要为每个项目定义这么一个函数,而不能放在一个库中共用。
在 c99 之后,则没有这个问题,cgo能够直接判断是内置的bool类型,能够直接转换为go的bool。
var bval_c C._Bool
var bval_go = bool(bval_c)