bea*_*mit 143 c shared-libraries dynamic-linking go
我正在尝试创建一个用Go程序编写的静态对象与C程序(比如内核模块或其他东西).
我已经找到了关于从Go调用C函数的文档,但是我没有找到关于如何走另一条道路的文档.我发现它是可能的,但很复杂.
这是我发现的:
有任何人对此有经验吗?简而言之,我正在尝试创建一个完全用Go编写的PAM模块.
jim*_*imt 121
你可以从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)) )
}
Run Code Online (Sandbox Code Playgroud)
调用所有内容的顺序如下:
foo.MyAdd(a, b) ->
C.doAdd(a, b) ->
C.goCallbackHandler(a, b) ->
foo.goCallbackHandler(a, b)
Run Code Online (Sandbox Code Playgroud)
这里要记住的关键是回调函数必须标记//export为Go侧的注释和externC侧的注释.这意味着您必须在包内定义要使用的任何回调.
为了允许包的用户提供自定义回调函数,我们使用与上面完全相同的方法,但我们提供用户的自定义处理程序(这只是一个常规的Go函数)作为传递给C的参数一边作为void*.然后由我们的包中的callbackhandler接收并调用它.
让我们使用我目前正在使用的更高级的示例.在这种情况下,我们有一个执行相当繁重任务的C函数:它从USB设备读取文件列表.这可能需要一段时间,因此我们希望我们的应用程序会收到有关其进度的通知.我们可以通过传入我们在程序中定义的函数指针来完成此操作.它只是在被调用时向用户显示一些进度信息.由于它有一个众所周知的签名,我们可以为它分配自己的类型:
type ProgressHandler func(current, total uint64, userdata interface{}) int
Run Code Online (Sandbox Code Playgroud)
此处理程序获取一些进度信息(当前接收的文件数和文件总数)以及一个接口{}值,该值可以容纳用户需要保存的任何内容.
现在我们需要编写C和Go管道以允许我们使用这个处理程序.幸运的是,我希望从库中调用的C函数允许我们传入一个类型的userdata结构void*.这意味着它可以容纳我们想要的任何东西,没有问题,我们将按原样将其恢复到Go世界.为了使所有这些工作,我们不直接从Go调用库函数,但我们为它创建一个C包装器,我们将命名它goGetFiles().正是这个包装器实际上将我们的Go回调提供给了C库,以及一个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"
Run Code Online (Sandbox Code Playgroud)
请注意,该goGetFiles()函数不会将回调函数指针作为参数.相反,我们的用户提供的回调被打包在一个自定义结构中,该结构包含该处理程序和用户自己的userdata值.我们将其goGetFiles()作为userdata参数传递给我们.
// 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 ))
}
Run Code Online (Sandbox Code Playgroud)
这就是我们的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
}
Run Code Online (Sandbox Code Playgroud)
这一切看起来都比现在复杂得多.与我们之前的示例相比,调用顺序没有改变,但是我们在链的末尾得到两个额外的调用:
订单如下:
foo.GetFiles(....) ->
C.goGetFiles(...) ->
C.somelib_get_files(..) ->
C.goProgressCB(...) ->
foo.goProgressCB(...) ->
main.myProgress(...)
Run Code Online (Sandbox Code Playgroud)
Ale*_*der 54
如果你使用gccgo,这不是一个令人困惑的主张.这适用于:
package main
func Add(a, b int) int {
return a + b
}
Run Code Online (Sandbox Code Playgroud)
#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);
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
36347 次 |
| 最近记录: |