然后所有的 C 代码 ,都可以通过 C.XXX 这样的方式来调用了,比如 C.putchar, C.malloc, C.free 等等。
这里,你应该能看出来 import 的 C 包是一个 "pseudo-package" 即『伪包』,是种黑魔法没错了,因为这里针对 cgo 进行了特殊对待,可以看作是一个命名空间。
而最关键的,莫过于参数的传递,C 与 Go 中的大部分基础类型都是可以互相转换的,不像 Node.js 或者 Python 中,需要包装一层成为专门的动态语言对象。CGO 中,它会将各种数据类型进行映射,比如 C 的 int 对应 go 的 int 或者 int32,C 的 float 对应 Go 的 float32 等等。
下面提两个比较特别,又是我们可能经常用到的例子:
CGO struct
而在 C 中的 struct,你可以使用 C.struct_example 的方式去定义[4],但同时也要注意,如果使用了 C 的 packed struct,则需要特殊处理[5]。
今天,我们来说说 cgo。
前言
在有些特殊的场景下,我们会有这样的困扰:
一般情况下,我们会倾向于使用这样几种方式去解决:
这里面的思路,无非是通过添加一层『胶水层』,将调用方与被调用方『粘合』在一起。
在取舍的时候,就看你们目前能承担的成本是多高了,比如人员不够的情况下,封装成 Web 服务耗费的精力可能比直接调用高,更遑论后期的运维成本了。
Node.js
于是,我们可以看到在 Node.js 中,有非常方便的 addons 集成方式,封装完成后,直接通过 require 的方式引入后调用即可。但由于需要针对平台进行编译,每次 Node.js 升级的时候,很容易导致模块被破坏,Node.js 直到 8.0 的时候,提供了 N-API 来保证 Node 本身大版本升级的时候,拓展的 C/C++ 模块依然能够使用。(对了,又是 DIP,通过依赖于抽象而不是具体实现,就能避免掉这种问题。)
另外,可以顺便提几个我们常用的 C/C++ 模块:
Node.js 的 addons 研究不多,以后有机会再说,有兴趣的也可以参考死月的《Node.js:来一打 C++ 扩展》。
Golang
Golang 中,就不得不提 cgo 了,相比 Node.js 的 addon 来说,似乎里面的黑魔法更多。
首先,在 Go 中调用 C 代码的方式是这样的:
然后所有的 C 代码 ,都可以通过
C.XXX
这样的方式来调用了,比如C.putchar, C.malloc, C.free
等等。这里,你应该能看出来 import 的 C 包是一个 "pseudo-package" 即『伪包』,是种黑魔法没错了,因为这里针对 cgo 进行了特殊对待,可以看作是一个命名空间。
而最关键的,莫过于参数的传递,C 与 Go 中的大部分基础类型都是可以互相转换的,不像 Node.js 或者 Python 中,需要包装一层成为专门的动态语言对象。CGO 中,它会将各种数据类型进行映射,比如 C 的 int 对应 go 的 int 或者 int32,C 的 float 对应 Go 的 float32 等等。
下面提两个比较特别,又是我们可能经常用到的例子:
CGO struct
而在 C 中的 struct,你可以使用
C.struct_example
的方式去定义[4],但同时也要注意,如果使用了 C 的 packed struct,则需要特殊处理[5]。CGO struct array
在 C 中返回 struct array 是个很常见的需求,我们可以有两种方式来处理:
在知道长度的情况下[6]:
不知道长度,动态 struct 长度,与上面相似:
CGO 编译
CGO 的编译,也是个黑魔法,需要将相关的编译选项写在注释里面:
如果引入其他的库,也可以使用
pkg-config
:另外,由于不同平台的编译选择可能不一样,那么还可以加上编译限制:
其中的逗号可以看作 and,而空格则表示 or,细节看这里。
样例
假如你想从具体的例子中,学到更多的内容,可以参考以下几个项目:
总结
CGO 虽然方便,但是在我看来有着很大的的不确定性,假如在有足够人员的情况下,还是尽量封装为 Web API 服务更靠谱,因为:
Ref