我可以通过CGo定义接受原生Go类型的C函数吗?

时间:2013-05-21 10:20:16

标签: go cgo

对于我正在与现有库集成的工作,我最终需要编写一些额外的C代码来提供可通过CGo使用的接口。

为了避免冗余数据副本,我希望能够将一些标准的Go类型(例如Go字符串)传递给这些C适配器函数。

我可以看到CGo生成的标题中定义了GoStringGoInterface类型,供导出的Go函数使用,但有没有办法在我自己的函数原型中使用这些类型的CGo会认出来吗?

目前,我最终在C原型中使用void *并在Go端传递unsafe.Pointer(&value)。这不像我想的那么干净(一方面,它使C代码能够写入值)。

更新

为了清楚起见,我确实知道Go的本机字符串类型和C char *之间的区别。我的观点是,因为我将复制传递给我的C函数的字符串数据,所以让Go端的代码自己复制是没有意义的。

我也明白,字符串布局可能会在未来的Go版本中发生变化,其大小可能因平台而异。但CGo已经通过它为我生成的记录的_cgo_export.h标题向我公开了与当前平台匹配的类型定义,所以谈论它未指定似乎有点奇怪:

typedef struct { char *p; int n; } GoString;

但是似乎没有办法在CGo可见的原型中使用这个定义。我并不过分担心二进制兼容性,因为使用此定义的代码将成为我的Go包的一部分,因此源级兼容性就足够了(如果更新包,那就不是那么重要了。事实并非如此)。

2 个答案:

答案 0 :(得分:1)

不是真的。您无法安全地混合,例如Go字符串(string)和C“字符串”(*char)代码,而不使用提供的帮助程序,即。 GoStringCString。原因是为了符合语言规范,必须在Go和C世界之间创建字符串内容的完整副本。不仅如此,垃圾收集器必须知道要考虑什么(Go字符串)和要忽略的内容(C字符串)。还有更多的事情要做,但让我在这里保持简单。

类似和/或其他限制/问题适用于其他Go“魔法”类型,例如mapinterface{}类型。在接口类型的情况下(但不仅仅是它),重要的是要意识到interface{}的内部实现(不仅仅是这种类型),不是指定的并且是特定于实现的。

这不仅仅是关于gc和gccgo之间可能存在的差异。它还意味着您的代码将在编译器开发人员决定更改(未指定且因此无保证)实现的某些细节时随时中断。

此外,即使Go(现在)没有使用压缩垃圾收集器,它也可能会发生变化而没有一些固定机制,任何直接访问Go运行时东西的代码都将再次注定失败。

结论:仅将简单实体作为C函数的参数传递。具有简单字段的POD结构也是安全的(指针字段通常不是)。从复杂的Go类型中,使用为Go字符串提供的帮助器,它们存在(非常好)。

答案 1 :(得分:1)

将Go字符串传递给C比应该更难。今天没有真正好的方法。请参阅https://golang.org/issue/6907

我今天所知道的最佳方法是

// typedef struct { const char *p; ptrdiff_t n; } gostring;
// extern CFunc(gostring s);
import "C"

func GoFunc(s string) {
    C.CFunc(*(*C.gostring)(unsafe.Pointer(&s)))
}

这当然假设字符串值的Go表示不会改变,这是不可保证的。