TonyChyi

我在那一角落患过抽风

让 C 语言使用 Go 的回调函数
2016年02月06日
 

 

封装 XMMS2 的过程中我遇到了一个问题

typedef int (*xmmsv_list_compare_func_t)(xmmsv_t **, xmmsv_t **);
int xmmsv_list_sort (xmmsv_t *listv, xmmsv_list_compare_func_t comparator) XMMS_PUBLIC;

这货居然用了回调(Callback)!

于是我尝试直接将 Go 函数的地址传给 C 代码,结果只有一个,段错误。因为 Cgo 只对打过 //export 注释的函数建立调用的 Stub

终于我还是在 Stack Overflow 的这个问题中受到了启发。

不要尝试直接调用 Go 函数,而是通过一个已经导出的函数间接的去调用它。

于是我写了一个名叫 callback 的包。

package callback

/*
void doTest(int);

typedef void (*callback)(int);

// 模拟一个需要 callback 的函数
static void real_func(callback func){
    func(9);  // BAKA
}

static void real_func_wrapper(){
    real_func(doTest);
}
*/
import "C"
import "sync"

type CallbackFunc func(int)

// 关键就在这里,需要设置一个全局变量
var w CallbackFunc

func init() {
    w = func(i int){}
}

//export doTest
func doTest(i C.int) {
    w(int(i))  // 通过已导出的 doTest() 来调用写好的 Callback
}

func SomeFunc(f CallbackFunc){
    lock := new(sync.Mutex)
    lock.Lock()
    defer lock.Unlock()
    w = f
    C.real_func_wrapper()
}

然后是测试用的程序

package main

import (
    "callback"
    "fmt"
)

func test(i int){
    fmt.Println("Got:", i)
}

func main() {
    callback.SomeFunc(test)
}

该程序在 Go 1.5.3 下运行成功

PS:在并发条件下,这个程序可能会出现问题,我需要进一步的思考这个问题

PPS:加锁就可以了嘛!!!

Tags: #Go · #C

 

TonyChyi © 2020 GPLv2