swig no es desconocido para C/C++
los . Es una herramienta de desarrollo utilizada para conectar el C/C++
proyecto actual con otros lenguajes de alto nivel. Para decirlo sin rodeos, puede generar api
código de interfaz para que otros lenguajes llamen . Entonces para Go
el proyecto, es que él puede generar directamente el código correspondiente para el C/C++
paquete público del proyecto , ya que los estudiantes de desarrollo de aplicaciones no necesitan empaquetar manualmente el código (otros lo escribieron por usted), reduciendo así el costo del acceso. fiesta.cgo
Go
cgo
Y swig
no solo está diseñado para el Go
idioma, sino que se adapta a muchos idiomas de alto nivel, como Python
, Java
, PHP
, Javascript
etc., puede generar la interfaz llamada por el idioma correspondiente, lo cual es conveniente para que la capa de aplicación acceda rápidamente.
Este artículo trata principalmente sobre la introducción swig
y Go
la integración de idiomas. Si también está interesado en otros idiomas, puede consultar la documentación oficial .
En aplicaciones prácticas, si se trata de un gran proyecto de C++, usar swig será aún más poderoso. Pero si se trata de un proyecto C pequeño, usar swig puede complicar las cosas, por lo que es mejor evaluar caso por caso.
instalar trago
linux
La instalación se puede descargar directamente desde el código fuente y compilar: www.swig.org/download.ht… , yum
también proporciona swig
el paquete, pero la versión predeterminada es relativamente antigua, macOS
puede usarla directamente brew install swig
para instalar. Si necesita elegir otras plataformas, puede consultar la documentación oficial: swig install
wget http://prdownloads.sourceforge.net/swig/swig-{versoin}.tar.gz && tar -zxvf
cd swig-*
./configure
make
sudo make install
复制代码
Usar el proceso de trago
El uso swig
no es muy complicado, y se divide principalmente en dos pasos:
- 定义
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
Ejemplo de código de llamadacgo
: github.com/swig/swig/t…swig
Documentación de llamadascgo
: Ejecución de SWIG con Go .