使用cgo将函数指针传递给C代码 [英] passing function pointer to the C code using cgo

查看:189
本文介绍了使用cgo将函数指针传递给C代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从Go v1.6开始,cgo更改了将指针传递给C代码的规则 golang/go#12416 .从Wiki的C代码调用动态Go回调的示例不再起作用.

Starting from Go v1.6 cgo changed the rules of passing pointers to the C code golang/go#12416. The example of invoking a dynamic Go callback from C code from the wiki doesn't work anymore.

package main

import (
    "fmt"
    "unsafe"
)

/*
   extern void go_callback_int(void* foo, int p1);

   // normally you will have to define function or variables
   // in another separate C file to avoid the multiple definition
   // errors, however, using "static inline" is a nice workaround
   // for simple functions like this one.
   static inline void CallMyFunction(void* pfoo) {
       go_callback_int(pfoo, 5);
       }
*/
import "C"

//export go_callback_int
func go_callback_int(pfoo unsafe.Pointer, p1 C.int) {
    foo := *(*func(C.int))(pfoo)
    foo(p1)
}

func MyCallback(x C.int) {
    fmt.Println("callback with", x)
}

// we store it in a global variable so that the garbage collector
// doesn't clean up the memory for any temporary variables created.
var MyCallbackFunc = MyCallback

func Example() {
    C.CallMyFunction(unsafe.Pointer(&MyCallbackFunc))
}

func main() {
    Example()
}

输出看起来像这样:

panic: runtime error: cgo argument has Go pointer to Go pointer

今天执行此操作的正确方法是什么?最好不要像将指针转换成uintptr_t那样从语言中隐藏指针这样的技巧.

What is the proper way to do this today? Preferably without hacks like hiding pointer from the language by converting it into uintptr_t.

推荐答案

从Go 1.6 cgo开始有新规则.

Starting from Go 1.6 cgo has new rules.

Go代码可以将Go指针传递给C,只要它所指向的Go存储器不包含任何Go指针即可.

Go code may pass a Go pointer to C provided that the Go memory to which it points does not contain any Go pointers.

[源代码]

在运行时检查这些规则,以及是否违反了程序崩溃.目前,可以使用 GODEBUG = cgocheck = 0 环境变量禁用检查.但是将来,这可能会停止工作.

These rules are checked during the runtime, and if violated program crashes. At the moment it is possible to disable checks using GODEBUG=cgocheck=0 environment variable. But in the future, that might stop working.

因此,如果它所指向的内存中存储了Go函数/方法指针,则无法再将指针传递给C代码.有几种方法可以克服此限制,但是我想在大多数方法中,您应该存储一个同步的数据结构,该数据结构表示某个ID与实际指针之间的对应关系.这样,您可以将id传递给C代码,而不是指针.

So it is not possible anymore to pass a pointer to C code, if the memory to which it is pointing stores a Go function/method pointer. There are several ways to overcome this limitations, but I guess in most of them you should store a synchronized data structure which represents the correspondence between a certain id and the actual pointer. This way you can pass an id to the C code, not a pointer.

解决此问题的代码如下:

The code solving this problem might look like this:

package gocallback

import (
    "fmt"
    "sync"
)

/*
extern void go_callback_int(int foo, int p1);

// normally you will have to define function or variables
// in another separate C file to avoid the multiple definition
// errors, however, using "static inline" is a nice workaround
// for simple functions like this one.
static inline void CallMyFunction(int foo) {
    go_callback_int(foo, 5);
}
*/
import "C"

//export go_callback_int
func go_callback_int(foo C.int, p1 C.int) {
    fn := lookup(int(foo))
    fn(p1)
}

func MyCallback(x C.int) {
    fmt.Println("callback with", x)
}

func Example() {
    i := register(MyCallback)
    C.CallMyFunction(C.int(i))
    unregister(i)
}

var mu sync.Mutex
var index int
var fns = make(map[int]func(C.int))

func register(fn func(C.int)) int {
    mu.Lock()
    defer mu.Unlock()
    index++
    for fns[index] != nil {
        index++
    }
    fns[index] = fn
    return index
}

func lookup(i int) func(C.int) {
    mu.Lock()
    defer mu.Unlock()
    return fns[i]
}

func unregister(i int) {
    mu.Lock()
    defer mu.Unlock()
    delete(fns, i)
}

此代码来自(更新后的) Wiki页面.

This code comes from the (updated) wiki page.

这篇关于使用cgo将函数指针传递给C代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆