gpt4 book ai didi

go - 使用 cgo 将函数指针传递给 C 代码

转载 作者:IT王子 更新时间:2023-10-29 01:18:28 27 4
gpt4 key购买 nike

从 Go v1.6 开始,cgo 改变了将指针传递给 C 代码的规则 golang/go#12416 .从 wiki 中的 C 代码调用动态 Go 回调的示例不再有效。

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 来隐藏语言中的指针。

最佳答案

从 Go 1.6 开始,cgo 有了新的规则。

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

[source]

这些规则在运行时被检查,如果违反则程序崩溃。目前可以使用 GODEBUG=cgocheck=0 环境变量来禁用检查。但在未来,这可能会失效。

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

解决这个问题的代码可能是这样的:

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 page .

关于go - 使用 cgo 将函数指针传递给 C 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37157379/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com