从C调用Go函数

我正在尝试创build一个用C语言编写的静态对象(比如说一个内核模块)。

我find了关于从Go调用C函数的文档,但我还没有find很多关于如何去其他方式。 我发现这是可能的,但是很复杂。

这是我发现的:

关于C和Go之间callback的博客文章

Cgo文档

Golang邮件列表文章

有没有人有这方面的经验? 总之,我试图创build一个完全用Go编写的PAM模块。

你可以从C中调用Go代码,这是一个令人困惑的提议。

该过程在您链接到的博客文章中列出。 但是我可以看到这不是很有帮助。 这是一个没有任何不必要的位的短片段。 它应该使事情更清楚一点。

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

所有被调用的顺序如下:

 foo.MyAdd(a, b) -> C.doAdd(a, b) -> C.goCallbackHandler(a, b) -> foo.goCallbackHandler(a, b) 

这里要记住的关键是一个callback函数必须用Go侧的//export注释和C侧的extern来标记。 这意味着您希望使用的任何callback都必须在您的包中定义。

为了让你的包的用户提供自定义的callback函数,我们使用与上面完全相同的方法,但是我们提供用户的自定义处理函数(这只是一个常规的Go函数)作为parameter passing到C方面作为void* 。 然后由我们的包中的回叫处理程序接收并调用。

让我们使用一个我正在使用的更高级的例子。 在这种情况下,我们有一个C函数执行相当繁重的任务:它从USB设备读取文件列表。 这可能需要一段时间,所以我们希望我们的应用程序被通知其进展。 我们可以通过传递我们在程序中定义的函数指针来实现这一点。 它只是简单地显示一些进度信息给用户,每当它被调用。 既然它有一个众所周知的签名,我们可以指定它自己的types:

 type ProgressHandler func(current, total uint64, userdata interface{}) int 

此处理程序会获取一些进度信息(当前接收的文件数量和文件总数)以及可以容纳用户需要的任何内容的接口{}值。

现在我们需要编写C和Go的pipe道,让我们使用这个处理程序。 幸运的是我希望从库调用的C函数允许我们传入一个void*types的userdata结构体。 这意味着它可以拥有任何我们想要的东西,不会问任何问题,我们将把它按原样返回到Go世界。 为了使所有这些工作,我们不直接从Go调用库函数,而是为它创build一个名为goGetFiles()的C封装器。 这是实际提供我们的Gocallback到C库,以及一个userdata对象的包装。

 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" 

请注意, goGetFiles()函数不会将callback函数指针作为参数。 相反,我们用户提供的callback函数被封装在一个自定义的结构体中,该结构体既包含该处理程序又包含用户自己的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 )) } 

这就是我们的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 } 

这一切看起来比现在复杂得多。 与我们前面的例子相比,呼叫顺序并没有改变,但是在链的末尾我们得到了两个额外的呼叫:

顺序如下:

 foo.GetFiles(....) -> C.goGetFiles(...) -> C.somelib_get_files(..) -> C.goProgressCB(...) -> foo.goProgressCB(...) -> main.myProgress(...) 

如果您使用gccgo,这不是一个令人困惑的命题。 这在这里工作:

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 

答案随着Go 1.5的发布而改变

这个我刚才问的SO问题是根据1.5增加的function再次解决的

在现有的C项目中使用Go代码

就我而言,这是不可能的:

注意:如果使用导出,则不能在前导码中定义任何C函数。

来源: https : //github.com/golang/go/wiki/cgo