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

huangapple go评论81阅读模式
英文:

Can I define C functions that accept native Go types through 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包的一部分,因此源代码级别的兼容性就足够了(如果不是这种情况,更新包也不是什么大问题)。

英文:

For the work I'm doing to integrate with an existing library, I ended up needing to write some additional C code to provide an interface that was usable through CGo.

In order to avoid redundant data copies, I would like to be able to pass some standard Go types (e.g. Go strings) to these C adapter functions.

I can see that there are GoString and GoInterface types defined in the header CGo generates for use by exported Go functions, but is there any way to use these types in my own function prototypes that CGo will recognise?

At the moment, I've ended up using void * in the C prototypes and passing unsafe.Pointer(&value) on the Go side. This is less clean than I'd like though (for one thing, it gives the C code the ability to write to the value).

Update:

Just to be clear, I do know the difference between Go's native string type and C char *. My point is that since I will be copying the string data passed into my C function anyway, it doesn't make sense to have the code on the Go side make its own copy.

I also understand that the string layout could change in a future version of Go, and its size may differ by platform. But CGo is already exposing type definitions that match the current platform to me via the documented _cgo_export.h header it generates for me, so it seems a bit odd to talk of it being unspecified:

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

But there doesn't seem to be a way to use this definition in prototypes visible to CGo. I'm not overly worried about binary compatibility, since the code making use of this definition would be part of my Go package, so source level compatibility would be enough (and it wouldn't be that big a deal to update the package if that wasn't the case).

答案1

得分: 1

不完全是。您不能安全地混合使用Go字符串(string)和C“字符串”(*char)的代码,而不使用提供的辅助函数,即GoStringCString。原因是为了符合语言规范,必须在Go和C世界之间进行字符串内容的完全复制。不仅如此,垃圾收集器必须知道要考虑什么(Go字符串)和要忽略什么(C字符串)。关于这个问题还有更多要做的事情,但让我在这里保持简单。

类似的限制/问题也适用于其他Go“神奇”类型,比如mapinterface{}类型。在接口类型的情况下(但不仅限于此),重要的是要意识到interface{}的内部实现(不仅限于此类型)是未指定的,并且是特定于实现的。

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

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

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

英文:

Not really. You cannot safely mix, for example Go strings (string) and C "strings" (*char) code without using the provided helpers for that, ie. GoString and CString. The reason is that to conform to the language specs a full copy of the string's content between the Go and C worlds must be made. Not only that, the garbage collector must know what to consider (Go strings) and what to ignore (C strings). And there are even more things to do about this, but let me keep it simple here.

Similar and/or other restrictions/problems apply to other Go "magical" types, like map or interface{} types. In the interface types case (but not only it), it's important to realize that the inner implementation of an interface{} (again not only this type), is not specified and is implementation specific.

That's not only about the possible differences between, say gc and gccgo. It also means that your code will break at any time the compiler developers decide to change some detail of the (unspecified and thus non guaranteed) implementation.

Additionally, even though Go doesn't (now) use a compacting garbage collector, it may change and without some pinning mechanism, any code accessing Go run time stuff directly will be again doomed.

Conclusion: Pass only simple entities as arguments to C functions. POD structs with simple fields are safe as well (pointer fields generally not). From the complex Go types, use the provided helpers for Go strings, they exists for a (very good) reason.

答案2

得分: 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字符串值的表示不会改变,这并不保证。

英文:

Passing a Go string to C is harder than it should be. There is no really good way to do it today. See https://golang.org/issue/6907.

The best approach I know of today is

// 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)))
}

This of course assumes that Go representation of a string value will not change, which is not guaranteed.

huangapple
  • 本文由 发表于 2013年5月21日 18:20:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/16667632.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定