swig is not unfamiliar to C/C++
the it. It is a development tool used to connect the current C/C++
project with other high-level languages. To put it bluntly, it can generate api
interface code for other languages to call . Then for Go
the project, it is that he can directly generate the corresponding code for the C/C++
public project package , as the students of application development do not need to manually package the code (others wrote it for you), thus reducing the cost of the access party.cgo
Go
cgo
And it swig
is not only designed for the Go
language, but adapted to many high-level languages, such as Python
, Java
, PHP
, Javascript
etc., can generate the interface called by the corresponding language, which is convenient for the application layer to quickly access.
This article is mainly about introduction swig
and Go
language integration. If you are also interested in other languages, you may wish to check the official documentation .
In practical applications, if it is a large C++ project, using swig will be even more powerful. But if it's a small C project, using swig can complicate things, so it's best to evaluate on a case-by-case basis.
install swig
linux
The installation can be downloaded directly from the source code and compiled: www.swig.org/download.ht… , yum
also provides swig
the package, but the default version is relatively old, macOS
you can use it directly brew install swig
to install. If you need to choose other platforms, you can check the official documentation: swig install
wget http://prdownloads.sourceforge.net/swig/swig-{versoin}.tar.gz && tar -zxvf
cd swig-*
./configure
make
sudo make install
复制代码
Use swig process
The use swig
is not very complicated, and it is mainly divided into two steps:
- 定义
swig
接口文件(swig interface file); - 使用
swig
命令行生成对应语言的代码; - 集成使用。
swig
接口文件
在使用swig
时,我们需要提供一个接口定义文件(SWIG interface file
),文件后缀往往是 .i
或者 .swig
,这个文件用来告诉swig
具体的操作逻辑,一个常规的接口文件大致内容如下:
/* File : example.i */
%module example
%{
/* Put headers and other declarations here */
extern double My_variable;
extern int fact(int);
extern int my_mod(int n, int m);
%}
extern double My_variable;
extern int fact(int);
extern int my_mod(int n, int m);
复制代码
/*...*/
代表的是注释部分。
%module
在go
中表示对应的 package
包名。最终对应生成的cgo
文件会转变为:
// example.go
package example
复制代码
%{...%}
代表额外要插入的头文件声明变量或函数声明(如果有),对应cgo
文件的如下部分区域,这和直接编写cgo
文件没有区别:
/*
extern void _wrap_My_variable_set_example_dffde00251d303fd(double arg1);
extern double _wrap_My_variable_get_example_dffde00251d303fd(void);
*/
import "C"
import "unsafe"
import _ "runtime/cgo"
复制代码
我们拿example.i
声明的My_variable
变量来说,你会发觉,最终生成的代码并没有直接是原生的My_variable
名称,而是提供了包裹的俩Getter
和 Setter
函数。swig
默认会对我们需要暴露的数据类型做一层包装,而且这些代码都是通过命令行工具结合 example.i
接口文件动态生成的,所以你不能直接修改这些代码。
之所以要包裹一层,是因为swig
需要做额外的数据管理,比如内存分配,内存释放等。使用方不用关心这些变量的内存管理问题,swig
来包装生成对应的内存管理函数方法。因为对于go
这种自动内存管理,和C
这种手动管理内存,通信数据的内存管理是不对等的,就像是前两节手撸cgo
代码提到的char *
等指针类型数据内存释放注意问题。
而example.i
剩下部分,则表示的是暴露给go
直接使用的函数方法,swig
会负责把如下三行生成对应的go
函数:
...
extern double My_variable;
extern int fact(int);
extern int my_mod(int n, int m);
复制代码
最终的example.go
会生成类似如下函数方法:
// 变量
func SetMy_variable(arg1 float64) {
_swig_i_0 := arg1
C._wrap_My_variable_set_example_dffde00251d303fd(C.double(_swig_i_0))
}
func GetMy_variable() (_swig_ret float64) {
var swig_r float64
swig_r = (float64)(C._wrap_My_variable_get_example_dffde00251d303fd())
return swig_r
}
// 函数
func Fact(arg1 int) (_swig_ret int) {
var swig_r int
_swig_i_0 := arg1
swig_r = (int)(C._wrap_fact_example_dffde00251d303fd(C.swig_intgo(_swig_i_0)))
return swig_r
}
// ...
复制代码
swig
默认会为每个变量声明一组Getter
和Setter
函数来操作这组数据,如果你希望数据是readonly
的,那么你需要添加额外的指令 %immutable
, 此指令只会生成Getter
函数。它对于定义在%{ %}
中和之外的变量都适用。
/* Some read-only variables */
%immutable;
%inline %{
extern double My_variable;
%}
extern double My_variable;
复制代码
swig 操作命令
swig
工具为每个集成的高级语言都有对于的命令行参数,拿Go
来说必要的参数时:
-go
-cgo
-intgosize
复制代码
如果我们希望把上述example.i
生成对应的cgo
文件,那么执行一下命令即可:
swig -go -cgo -intgosize 64 example.i
复制代码
最终会生成如下的文件:
example/
├── example.go
├── example.i
└── example_wrap.c
复制代码
example.go
即是最终需要在Go
语言集成的代码文件,而配套的example_wrap.c
则是需要在你的公共库一起编译的文件,二者缺一不可。
上述参数中 intgosize
需要告诉生成工具当前go
编译整型字节长度是64位架构的还是32位的,这点比较特殊,究其原因,我们也大致能猜到,Go
语言中尤其是64位操作系统中int
所占字节和C
语言所占字节数是不一样的,需要额外适配,使用原生cgo编码也会遇到样的问题。
其他参数说明
参数 | 说明 |
---|---|
-cgo | 生成文件将作为go tool cgo 输入使用,go1.5 版本之后可用,未来会考虑内置为默认参数 |
-intgosize |
设置Go int 类型长度,s 取值位 32 或64 |
-package | 设置导出文件包名,类似%module |
-use-shlib | 生成共享库so,再需要把go项目导出为共享库是才有用 |
-soname | 共享库名称 |
-gccgo | 针对选择gccgo 编译时,默认只生成基于gc 编译器文件 |
-go-pkgpath | 针对选择gccgo 编译器时,设置的pkgpath |
-go-prefix | 针对选择gccgo 编译器时,设置路径前缀。如果-go-pkgpath 存在,则此变量忽略 |
不过,在实践中,我们发觉不使用-cgo
也是可行的,因为一般我们直接会使用go build
直接构建,它内部会自动识别cgo
代码而自动选择cgo
工具来处理。另外对于编译Go
代码我们一般都是使用内置go compiler
默认编译器,所以相关的gccgo
编译器其实使用场景不多。
集成使用
有了对swig
基础了解后,针对上述的演示项目,假设我们有一个C
接口函数需要暴露给Go
使用:
char* getName()
复制代码
那么我们只需要在C
库实现这个函数功能,例如:
char* getName(){
return "hello the swig";
}
复制代码
把这个文件命名为example.c
, 并且和example.i
都放在 example/
路径下,然后声明 example.i
接口:
%module example
%{
extern char* getName();
%}
/*暴露给Go使用的函数*/
extern char* getName();
复制代码
请注意上面的代码,暴露给Go
函数一定要和%{...%}
中声明的函数配套,缺一不可。因为最终生成的文件实质就是在调用%{...%}
代码块中声明的函数。
从上图我们可以看出(红色箭头)表示最终生成的代码,而真正的调用过程也变成了绿色箭头所示的步骤。
然后运行:
swig -go -intgosize 64 example/example.i
复制代码
最终的目录文件结构如下:
example
├── example.c
├── example.go
├── example.i
└── example_wrap.c
复制代码
之后便是直接在 Go
代码直接使用这个包目录即可。
package main
import (
"cgo/example"
"fmt"
)
func main() {
fmt.Println(example.GetName())
}
复制代码
最终输出如下结果:
# go run main.go
hello the swig
复制代码
相关资料
swig
Callingcgo
code example: github.com/swig/swig/t…swig
Callingcgo
documentation: Running SWIG with Go .