点击上方蓝色“ Go语言中文网 ”关注我们, 领全套Go资料 ,每天学习 Go 语言
Cgo
允许 Go 程序调用 C 库或其他暴露了 C 接口的库。正是如此,这也成为 Go 程序员工具箱的重要组成部分。
使用Cgo
可能会比较棘手,特别是在 Go 和 C 代码中传递指针和回调函数时。这篇文章讨论了一个端到端当例子,包含了如下几方面:
Cgo
的基本使用,包括链接一个传统的 C 库到 Go 二进制文件中。- 从 Go 语言中传递 struct 到 C 语言中。
- 传递 Go 函数到 C 程序中,并安排 C 程序在随后调用它们。
- 安全的传递任意的 Go 数据到 C 代码中,这些 C 代码后续会回传这些数据到它所调用的 Go 回调中。
本文并不是一个Cgo
的使用教程-在阅读前,需要你对它对简单使用案例有所熟悉。在本文最后列了一些有用的Cgo
使用教程和相关的文章。这个案例的全部源代码详见Github[1]。
问题所在-一个 C 库调用多个 Go 回调程序
如下是一个虚构的 C 库的头文件,该库处理(输入)数据,并基于事件调用回调函数。
typedef void (*StartCallbackFn)(void* user_data, int i);
回调标签是由几个重要的模式组成,所展示的这些模式在现实中也同样普遍:
- 每一个回调拥有自己的类型签名,这里为了简便,我们使用
int
类型的参数,这个参数可以是其他任何类型。 - 当只有较小数量的回调被调用时,它们可能作为独立的参数被传递到 traverse 中;然而,回调的数量非常大时(比如说,超过三个),后几乎总是有一个汇集它们的结构体被传递。允许用户将某些回调参数设置为 null 很常见,以向底层库传达:对于某些特定事件并没有意义,也不应为此调用任何用户代码。
- 每个回调都获得一个不透明指针 user_data,该指针从调用者传递到 traverse (最终传递到回调函数)。它用于区分互不相同的遍历,并传递用户特定的状态。典型的,traverse 会透传 user_data,而不尝试访问他;由于它是
void *
,因此它对于库是完全模糊的, 并且用户代码会将其强制转换为回调中的某些具体类型。