从 C 调用 Go 函数

我正在尝试创建一个静态对象写在去接口与 C 程序(比如,一个内核模块或东西)。

我已经找到了关于从 Go 调用 C 函数的文档,但是没有找到关于如何从 Go 调用 C 函数的更多信息。我发现这是有可能的,但是很复杂。

以下是我的发现:

关于 C 和 Go 之间回调的博客文章

海关总署文件

戈兰邮寄名单邮寄

有人有这方面的经验吗? 简而言之,我正在尝试创建一个完全用 Go 编写的 PAM 模块。

52511 次浏览

您可以从 C 调用 Go 代码,但这是一个令人困惑的命题。

在您链接到的博客文章中概述了这个过程。但我知道这没什么帮助。下面是一个简短的片段,没有任何不必要的位。应该能让事情更清楚一点。

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)) )
}

每样东西的调用顺序如下:

foo.MyAdd(a, b) ->
C.doAdd(a, b) ->
C.goCallbackHandler(a, b) ->
foo.goCallbackHandler(a, b)

这里要记住的关键是,回调函数必须在 Go 端标记为 //export注释,在 C 端标记为 extern。这意味着您希望使用的任何回调都必须在包中定义。

为了允许包的用户提供一个自定义回调函数,我们使用与上面完全相同的方法,但是我们提供用户的自定义处理程序(这只是一个常规 Go 函数)作为参数,作为 void*传递到 C 端。然后它被包中的 callbackhandle 接收并调用。

让我们使用我目前正在处理的一个更高级的示例。在本例中,我们有一个 C 函数,它执行一项相当繁重的任务: 从 USB 设备读取文件列表。这可能需要一段时间,所以我们希望我们的应用程序将其进展通知。我们可以通过在程序中定义一个函数指针来实现这一点。它只是在用户调用时显示一些进度信息。由于它有一个众所周知的签名,我们可以为它指定自己的类型:

type ProgressHandler func(current, total uint64, userdata interface{}) int

该处理程序接收一些进度信息(当前接收的文件数和文件总数)以及一个接口{}值,该值可以保存用户需要保存的任何内容。

现在我们需要编写 C 和 Go 管道来允许我们使用这个处理程序。幸运的是,我希望从库中调用的 C 函数允许我们传入类型为 void*的 userdata 结构。这意味着它可以容纳任何我们想让它容纳的东西,没有任何问题,我们将让它回到围棋世界的原样。为了实现所有这些功能,我们不直接从 Go 调用库函数,而是为它创建一个 C 包装器,我们将其命名为 goGetFiles()。正是这个包装器实际上提供了对 C 库的 Go 回调以及一个 userdata 对象。

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"

请注意,goGetFiles()函数不接受回调的任何函数指针作为参数。相反,我们的用户提供的回调被打包在一个自定义结构中,该结构同时包含该处理程序和用户自己的用户数据值。我们将其作为 userdata 参数传递给 goGetFiles()

// 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 ))
}

这就是我们的 C 绑定。用户代码现在非常直接:

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%%)\n", userdata.(string), current, total, fc / ft)
return 0
}

这一切看起来比实际情况要复杂得多。与前面的示例相比,呼叫顺序没有改变,但是我们在链的末尾得到了两个额外的呼叫:

命令如下:

foo.GetFiles(....) ->
C.goGetFiles(...) ->
C.somelib_get_files(..) ->
C.goProgressCB(...) ->
foo.goProgressCB(...) ->
main.myProgress(...)

如果您使用 gccgo,这不是一个令人困惑的命题:

走吧

package main


func Add(a, b int) int {
return a + b
}

Barc

#include <stdio.h>


extern int go_add(int, int) __asm__ ("example.main.Add");


int main() {
int x = go_add(2, 3);
printf("Result: %d\n", x);
}

马克菲尔

all: main


main: foo.o bar.c
gcc foo.o bar.c -o main


foo.o: foo.go
gccgo -c foo.go -o foo.o -fgo-prefix=example


clean:
rm -f main *.o

在我看来,这是不可能的:

注意: 如果使用 出口。

来源: https://github.com/golang/go/wiki/cgo

答案随着 Go 1.5的发布而改变

我前段时间提出的这个 SO 问题再次根据1.5增加的功能解决了这个问题

在现有的 C 项目中使用 Go 代码