3 回答

TA貢獻1890條經(jīng)驗 獲得超9個贊
從 Go 1.6 開始,cgo 有了新的規(guī)則。
Go 代碼可以將 Go 指針傳遞給 C,前提是它指向的 Go 內存不包含任何 Go 指針。
在運行時檢查這些規(guī)則,如果違反程序崩潰。目前可以使用GODEBUG=cgocheck=0環(huán)境變量禁用檢查。但在未來,這可能會停止工作。
因此,如果指針指向的內存存儲了 Go 函數(shù)/方法指針,則無法再將指針傳遞給 C 代碼。有幾種方法可以克服這個限制,但我想在大多數(shù)方法中你應該存儲一個同步的數(shù)據(jù)結構,它表示某個 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 頁面。

TA貢獻1873條經(jīng)驗 獲得超9個贊
解決方案分為三個部分。
//export
在 Go 函數(shù)上使用注釋告訴cgo
工具為其生成 C 包裝器 ( https://pkg.go.dev/cmd/cgo#hdr-C_references_to_Go )。在
cgo
C 序言中前向聲明該 C 標識符,以便您可以在 Go 代碼 ( https://golang.org/issue/19837 ) 中引用 C 包裝器。使用 C
typedef
將該指針轉換為正確的 C 類型,解決一個cgo
錯誤 ( https://golang.org/issue/19835 )。
把它們放在一起:
package main
/*
static void invoke(void (*f)()) {
f();
}
void go_print_hello(); // https://golang.org/issue/19837
typedef void (*closure)(); // https://golang.org/issue/19835
*/
import "C"
import "fmt"
//export go_print_hello
func go_print_hello() {
fmt.Println("Hello, ?!")
}
func main() {
C.invoke(C.closure(C.go_print_hello))
}

TA貢獻1773條經(jīng)驗 獲得超3個贊
這完全取決于您需要對回調函數(shù)執(zhí)行什么操作 - 但一個可能有效的技巧是不傳遞 Go 函數(shù),而是傳遞一個指向帶有您想要的函數(shù)的結構的指針。
例如:
package main
import (
"fmt"
"unsafe"
)
/*
extern void go_callback_int(void*, int);
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 := (*Test)(pfoo)
foo.cb(p1)
}
type Test struct {
}
func (s *Test) cb(x C.int) {
fmt.Println("callback with", x)
}
func main() {
data := &Test{}
C.CallMyFunction(unsafe.Pointer(&data))
}
另一種選擇可能是使用示例中的全局變量,然后放棄在指向 C 的指針中傳遞任何有用的內容。
像這樣:
package main
import (
"fmt"
"unsafe"
)
/*
extern void go_callback_int(void*, int);
static inline void CallMyFunction(void* pfoo) {
go_callback_int(pfoo, 5);
}
*/
import "C"
//export go_callback_int
func go_callback_int(_ unsafe.Pointer, p1 C.int) {
MyCallbackFunc(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 main() {
C.CallMyFunction(nil)
}
這可能取決于您的 C 回調需要做什么。
如果這些都不起作用,則可以使用通道做一些技巧來發(fā)送您需要的值(并將其保持在范圍內)。
- 3 回答
- 0 關注
- 630 瀏覽
添加回答
舉報