从 C 调用 Go 函数

从 C 调用 Go 函数,第1张

从 C 调用 Go 函数

您可以从 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 端和
extern
C 端标记注释。这意味着您希望使用的任何回调都必须在您的包中定义。

为了允许包的用户提供自定义回调函数,我们使用与上面完全相同的方法,但我们提供用户的自定义处理程序(这只是一个常规的 Go 函数)作为传递到 C 的参数边作为

void*
。然后由我们包中的回调处理程序接收并调用。

让我们使用我目前正在使用的更高级的示例。在本例中,我们有一个 C 函数执行一项非常繁重的任务:它从 USB 设备读取文件列表。这可能需要一段时间,所以我们希望我们的应用程序收到它的进度通知。我们可以通过传入我们在程序中定义的函数指针来做到这一点。它只是在被调用时向用户显示一些进度信息。由于它有一个众所周知的签名,我们可以为它分配自己的类型:

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

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

现在我们需要编写 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"

请注意,该

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(...)


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5601982.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-15

发表评论

登录后才能评论

评论列表(0条)

保存