4

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

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

我可以看到 CGo 生成的头文件中定义了一些类型GoStringGoInterface供导出的 Go 函数使用,但是有什么方法可以在 CGo 识别的我自己的函数原型中使用这些类型?

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

更新:

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

我也知道字符串布局在 Go 的未来版本中可能会发生变化,并且它的大小可能因平台而异。但是 CGo 已经通过_cgo_export.h它为我生成的文档标头向我公开了与当前平台匹配的类型定义,因此谈论它未指定似乎有点奇怪:

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

但是似乎没有办法在 CGo 可见的原型中使用这个定义。我并不太担心二进制兼容性,因为使用这个定义的代码将是我的 Go 包的一部分,所以源代码级别的兼容性就足够了(如果那样的话,更新包也没什么大不了的情况并非如此)。

4

2 回答 2

1

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

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

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

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

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

于 2013-05-21T10:36:48.047 回答
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 表示不会改变,这是不能保证的。

于 2017-06-11T04:26:11.687 回答