C语言 从 C 调用 Go 函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6125683/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Call Go functions from C
提问by beatgammit
I am trying to create a static object written in Go to interface with a C program (say, a kernel module or something).
我正在尝试创建一个用 Go 编写的静态对象,以与 C 程序(例如内核模块或其他东西)交互。
I have found documentation on calling C functions from Go, but I haven't found much on how to go the other way. What I've found is that it's possible, but complicated.
我找到了关于从 Go 调用 C 函数的文档,但我没有找到太多关于如何走另一条路的信息。我发现这是可能的,但很复杂。
Here is what I found:
这是我发现的:
Blog post about callbacks between C and Go
Does anyone have experience with this? In short, I'm trying to create a PAM module written entirely in Go.
有任何人对此有经验吗?简而言之,我正在尝试创建一个完全用 Go 编写的 PAM 模块。
采纳答案by jimt
You can call Go code from C. it is a confusing proposition though.
您可以从 C 中调用 Go 代码。不过,这是一个令人困惑的命题。
The process is outlined in the blog post you linked to. But I can see how that isn't very helpful. Here is a short snippet without any unnecessary bits. It should make things a little clearer.
您链接到的博客文章中概述了该过程。但我可以看出这不是很有帮助。这是一个没有任何不必要位的简短片段。它应该让事情更清楚一点。
package foo
// extern int goCallbackHandler(int, int);
//
// static int doAdd(int a, int b) {
// return goCallbackHandler(a, b);
// }
import "C"
//export goCallbackHandler
func goCallbackHandler(a, b C.int) C.int {
return a + b
}
// This is the public function, callable from outside this package.
// It forwards the parameters to C.doAdd(), which in turn forwards
// them back to goCallbackHandler(). This one performs the addition
// and yields the result.
func MyAdd(a, b int) int {
return int( C.doAdd( C.int(a), C.int(b)) )
}
The order in which everything is called is as follows:
调用所有内容的顺序如下:
foo.MyAdd(a, b) ->
C.doAdd(a, b) ->
C.goCallbackHandler(a, b) ->
foo.goCallbackHandler(a, b)
The key to remember here is that a callback function must be marked with the //exportcomment on the Go side and as externon the C side. This means that any callback you wish to use, must be defined inside your package.
这里要记住的关键是回调函数必须//export在 Go 端和externC 端用注释标记。这意味着您希望使用的任何回调都必须在您的包中定义。
In order to allow a user of your package to supply a custom callback function, we use the exact same approach as above, but we supply the user's custom handler (which is just a regular Go function) as a parameter that is passed onto the C side as void*. It is then received by the callbackhandler in our package and called.
为了允许包的用户提供自定义回调函数,我们使用与上面完全相同的方法,但我们提供用户的自定义处理程序(这只是一个常规的 Go 函数)作为传递到 C 的参数边作为void*。然后由我们包中的回调处理程序接收并调用。
Let's use a more advanced example I am currently working with. In this case, we have a C function that performs a pretty heavy task: It reads a list of files from a USB device. This can take a while, so we want our app to be notified of its progress. We can do this by passing in a function pointer that we defined in our program. It simply displays some progress info to the user whenever it gets called. Since it has a well known signature, we can assign it its own type:
让我们使用我目前正在使用的更高级的示例。在本例中,我们有一个 C 函数执行一项非常繁重的任务:它从 USB 设备读取文件列表。这可能需要一段时间,所以我们希望我们的应用程序收到它的进度通知。我们可以通过传入我们在程序中定义的函数指针来做到这一点。它只是在被调用时向用户显示一些进度信息。由于它有一个众所周知的签名,我们可以为它分配自己的类型:
type ProgressHandler func(current, total uint64, userdata interface{}) int
This handler takes some progress info (current number of files received and total number of files) along with an interface{} value which can hold anything the user needs it to hold.
此处理程序获取一些进度信息(当前接收的文件数和文件总数)以及一个接口值,该值可以保存用户需要保存的任何内容。{}
Now we need to write the C and Go plumbing to allow us to use this handler. Luckily the C function I wish to call from the library allows us to pass in a userdata struct of type void*. This means it can hold whatever we want it to hold, no questions asked and we will get it back into the Go world as-is. To make all this work, we do not call the library function from Go directly, but we create a C wrapper for it which we will name goGetFiles(). It is this wrapper that actually supplies our Go callback to the C library, along with a userdata object.
现在我们需要编写 C 和 Go 管道以允许我们使用这个处理程序。幸运的是,我希望从库中调用的 C 函数允许我们传入类型为 的 userdata 结构void*。这意味着它可以容纳我们想要它容纳的任何东西,没有任何问题,我们将按原样将其带回围棋世界。为了使所有这些工作,我们不直接从 Go 调用库函数,而是为它创建一个 C 包装器,我们将其命名为goGetFiles()。正是这个包装器实际向 C 库提供了我们的 Go 回调以及一个用户数据对象。
package foo
// #include <somelib.h>
// extern int goProgressCB(uint64_t current, uint64_t total, void* userdata);
//
// static int goGetFiles(some_t* handle, void* userdata) {
// return somelib_get_files(handle, goProgressCB, userdata);
// }
import "C"
import "unsafe"
Note that the goGetFiles()function does not take any function pointers for callbacks as parameters. Instead, the callback that our user has supplied is packed in a custom struct that holds both that handler and the user's own userdata value. We pass this into goGetFiles()as the userdata parameter.
请注意,该goGetFiles()函数不会将任何用于回调的函数指针作为参数。相反,我们的用户提供的回调被打包在一个自定义结构中,该结构包含该处理程序和用户自己的 userdata 值。我们将其goGetFiles()作为 userdata 参数传入。
// This defines the signature of our user's progress handler,
type ProgressHandler func(current, total uint64, userdata interface{}) int
// This is an internal type which will pack the users callback function and userdata.
// It is an instance of this type that we will actually be sending to the C code.
type progressRequest struct {
f ProgressHandler // The user's function pointer
d interface{} // The user's userdata.
}
//export goProgressCB
func goProgressCB(current, total C.uint64_t, userdata unsafe.Pointer) C.int {
// This is the function called from the C world by our expensive
// C.somelib_get_files() function. The userdata value contains an instance
// of *progressRequest, We unpack it and use it's values to call the
// actual function that our user supplied.
req := (*progressRequest)(userdata)
// Call req.f with our parameters and the user's own userdata value.
return C.int( req.f( uint64(current), uint64(total), req.d ) )
}
// This is our public function, which is called by the user and
// takes a handle to something our C lib needs, a function pointer
// and optionally some user defined data structure. Whatever it may be.
func GetFiles(h *Handle, pf ProgressFunc, userdata interface{}) int {
// Instead of calling the external C library directly, we call our C wrapper.
// We pass it the handle and an instance of progressRequest.
req := unsafe.Pointer(&progressequest{ pf, userdata })
return int(C.goGetFiles( (*C.some_t)(h), req ))
}
That's it for our C bindings. The user's code is now very straight forward:
这就是我们的 C 绑定。用户的代码现在非常简单:
package main
import (
"foo"
"fmt"
)
func main() {
handle := SomeInitStuff()
// We call GetFiles. Pass it our progress handler and some
// arbitrary userdata (could just as well be nil).
ret := foo.GetFiles( handle, myProgress, "Callbacks rock!" )
....
}
// This is our progress handler. Do something useful like display.
// progress percentage.
func myProgress(current, total uint64, userdata interface{}) int {
fc := float64(current)
ft := float64(total) * 0.01
// print how far along we are.
// eg: 500 / 1000 (50.00%)
// For good measure, prefix it with our userdata value, which
// we supplied as "Callbacks rock!".
fmt.Printf("%s: %d / %d (%3.2f%%)\n", userdata.(string), current, total, fc / ft)
return 0
}
This all looks a lot more complicated than it is. The call order has not changed as opposed to our previous example, but we get two extra calls at the end of the chain:
这一切看起来比实际复杂得多。与我们之前的示例相比,调用顺序没有改变,但我们在链的末尾获得了两个额外的调用:
The order is as follows:
顺序如下:
foo.GetFiles(....) ->
C.goGetFiles(...) ->
C.somelib_get_files(..) ->
C.goProgressCB(...) ->
foo.goProgressCB(...) ->
main.myProgress(...)
回答by Alexander
It is not a confusing proposition if you use gccgo. This works here:
如果您使用 gccgo,这不是一个令人困惑的提议。这在这里工作:
foo.go
foo.go
package main
func Add(a, b int) int {
return a + b
}
bar.c
酒吧
#include <stdio.h>
extern int go_add(int, int) __asm__ ("example.main.Add");
int main() {
int x = go_add(2, 3);
printf("Result: %d\n", x);
}
Makefile
生成文件
all: main
main: foo.o bar.c
gcc foo.o bar.c -o main
foo.o: foo.go
gccgo -c foo.go -o foo.o -fgo-prefix=example
clean:
rm -f main *.o
回答by dreadiscool
The answer has changed with the release of Go 1.5
随着 Go 1.5 的发布,答案发生了变化
This SO question that I asked some time ago addresses the issue again in light of the 1.5 added capabilities
鉴于 1.5 添加的功能,我前段时间问的这个 SO 问题再次解决了这个问题
回答by Rafael Ribeiro
As far as I am concerned it isn't possible:
就我而言,这是不可能的:
Note: you can't define any C functions in preamble if you're using exports.
注意:如果您使用导出,则不能在序言中定义任何 C 函数。

