您可以从C调用Go代码。但这是一个令人困惑的主张。
您链接到的博客文章中概述了该过程。但是我可以看到这不是很有帮助。这是一个简短的片段,没有任何不必要的内容。它应该使事情更加清晰。
package foo// extern int goCallbackHandler(int, int);//// static int doAdd(int a, int b) {// return goCallbackHandler(a, b);// }import "C"//export goCallbackHandlerfunc 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)
这里要记住的关键是,回调函数必须
//export在Go侧和
externC侧都标记有注释。这意味着您要使用的任何回调都必须在包中定义。
为了允许您的包用户提供自定义回调函数,我们使用与上述方法完全相同的方法,但是我们提供了用户的自定义处理程序(只是常规的Go函数)作为传递给C的参数侧为
void*。然后,它由我们的包中的回调处理程序接收并调用。
让我们使用我目前正在使用的更高级的示例。在这种情况下,我们有一个C函数来执行一项繁重的任务:它从USB设备读取文件列表。这可能需要一段时间,因此我们希望通知我们的应用程序进度。我们可以通过传入我们在程序中定义的函数指针来做到这一点。每当调用它时,它仅向用户显示一些进度信息。由于它具有众所周知的签名,因此我们可以为其分配自己的类型:
type ProgressHandler func(current, total uint64, userdata interface{}) int
该处理程序获取一些进度信息(当前接收的文件数和文件总数)以及interface {}值,该值可以容纳用户需要保存的任何内容。
现在,我们需要编写C和Go管道,以允许我们使用此处理程序。幸运的是,我希望从库中调用的C函数允许我们传递type的userdata结构
void*。这意味着它可以容纳我们想要容纳的任何东西,不问任何问题,我们将其按原样带回到Go世界。为了使所有这些工作正常进行,我们没有直接从Go中调用库函数,而是为其创建了一个C包装器,我们将其命名为
goGetFiles()。实际上,此包装器将我们的Go回调与userdata对象一起提供给C库。
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()函数不将回调的任何函数指针作为参数。相反,我们的用户提供的回调包含在一个自定义结构中,该结构既包含该处理程序,又包含用户自己的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 pre.type progressRequest struct { f ProgressHandler // The user's function pointer d interface{} // The user's userdata.}//export goProgressCBfunc 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 mainimport ( "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(...)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)