douchuoliu4422 2011-05-25 14:02
浏览 65
已采纳

从C调用Go函数

I am trying to create a static object written in Go to interface with a C program (say, a kernel module or something).

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.

Here is what I found:

Blog post about callbacks between C and Go

Cgo documentation

Golang mailing list post

Does anyone have experience with this? In short, I'm trying to create a PAM module written entirely in Go.

  • 写回答

4条回答 默认 最新

  • duanmeng3573 2011-05-27 02:02
    关注

    You can call Go code from C. it is a confusing proposition though.

    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 //export comment on the Go side and as extern on the C side. This means that any callback you wish to use, must be defined inside your package.

    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.

    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:

    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.

    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.

    // 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:

    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%%)
    ", 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(...)
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

悬赏问题

  • ¥15 c程序不知道为什么得不到结果
  • ¥40 复杂的限制性的商函数处理
  • ¥15 程序不包含适用于入口点的静态Main方法
  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置