回答

收藏

从 C 调用 Go 函数

技术问答 技术问答 490 人阅读 | 0 人回复 | 2023-09-12

我正在尝试创建一个用 Go 编写的静态对象与 C 程序(如核模块或其他东西)交互。, s, j1 u+ R0 ]. _& i
我找到了从 Go 调用 C 函数文档,但是我没有找到太多关于如何走另一条路的信息。我发现这是可能的,但是很复杂。  F! F5 i; N$ _+ Q5 |1 ~$ f2 D
                                                               
8 G9 o- {/ U( M7 j    解决方案:                                                                ' @) a3 \( n8 o, L! X$ y% e2 f
                                                                您可以从 C 中调用 Go 代码。然而,这是一个令人困惑的命题。
4 a% c5 V6 `! w: W6 E" k8 M$ y你链接到的博客文章总结了这个过程。但我可以看到这并不是很有帮助。这是一个没有任何不必要的短片。它应该让事情更清楚。
" Z4 Z: G5 O; C  h5 `! A6 _
    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)) )}
    , p- P) O5 g+ L& A. d3 U7 Q
调用所有内容的顺序如下:
% r& i1 d  }7 G: T- Q% t% G( `! F
    foo.MyAdd(a,b) ->  C.doAdd(a,b) ->    C.goCallbackHandler(a,b) ->      foo.goCallbackHandler(a,b)+ N6 q6 s0 M5 C2 h5 T) ?
这里要记住的关键是回调函数必须///export在 Go 端和externC 端标记注释。这意味着您想要使用的任何回调都必须在您的包中定义。
) e& G. |6 }1 }  S" i我们使用与上述方法完全相同的方法来允许包用户提供自定义回调函数,但我们提供用户的自定义处理程序只是一个常规的 Go作为一种传递到  函数)C 作为参数边void*。接收并调用我们包中的回调处理程序。; n( b5 s, L6 R+ z! P5 C
让我们用我目前正在使用的更先进的例子。在这种情况下,我们有一个 C 函数执行一项非常繁重的任务:从 USB 设备读取文件列表。这可能需要一段时间,所以我们希望我们的应用程序收到它的进度通知。我们可以通过在程序中引入我们定义的函数指针来做到这一点。它只是在被调用时向用户显示一些进度信息。因为它有一个众所周知的签名,我们可以分配自己的类型:
% O8 R' O" k! G" V2 t# {2 H! i
    type ProgressHandler func(current,total uint64,userdata interface{}) int
    1 o9 t( _1 {) x8 ^8 F# k- n
该处理程序获取一些进度信息(当前文件数和文件总数)和一个接口值,可以保存用户需要保存的任何内容。$ Y1 B1 x+ J- I6 q7 O' p
现在我们需要写 C 和 Go 管道允许我们使用此处理程序。幸运的是,我希望从库中调用 C 函数允许我们引入  userdata 结构void*。这意味着它可以容纳我们想要的任何东西,没有问题,我们会把它带回围棋世界。为了使所有这些工作,我们不直接从 Go 调用库函数,创建 C 包装器,我们叫它goGetFiles()。正是这个包装器实际上向 。C 库提供了我们的 Go 回调和用户数据对象。
: A1 i7 l8 L4 Y) X. D
    package foo// #include // 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"8 }# U, F6 O' [3 M& y" t1 `
请注意,该goGetFiles()函数不会以任何用于回调的函数指针为参数。相反,我们用户提供的回调包含在用户自己的处理程序和 的自定义结构中userdata 值。我们会的goGetFiles()作为 userdata 参数传入。: M: Y" _: ], X* w2 u

    5 Z) b! s3 z  K. S+ A9 Q
  • // 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 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)(//))()())()()())()())())()())()))())())())())()))())())()))()))()))()))())()))()))()))()()))()))()))())()))())()))()))()())())())())()))()))()())()))())())())())()())())())()()))())())()())()())())()))()())())())()()())()())()))())()))())())())))()()()()())()))())())()))())())()()()()()()))())))))()()()())()))())))()()()()())())))()))))())))()))())()()))))()()()()())()))())))))()()()()()())))))))))))))))()()()()()))))()()())())()))())))())()()())))))())))()()()()()()()()))))))))()))))))()()))))()())())))))()())())()()()()()())()())))))))))))))))()()(())))(()))))))))))(((())((())))(()())))))(code]这是我们的 C 绑定。用户的代码现在非常简单:[code]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 .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}5 N( f; A, q0 ^, w6 M( {5 a9 ]
所有这些似乎都比实际复杂得多。与我们之前的例子相比,调用顺序没有改变,但我们在链的末尾获得了两个额外的调用:
* J$ {; C0 `1 ]顺序如下:: f- l' X2 F: `2 U4 J2 H
    foo.GetFiles(....) ->  C.goGetFiles(...) ->    C.somelib_get_files(..) ->      C.goProgressCB(...) ->        foo.goProgressCB(...) ->           main.myProgress(...)* s4 t' U$ C' G$ \; _9 `  f
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则