swigは、それを書いC/C++
たなじみがありません。これは、現在のC/C++
プロジェクトを他の高級言語と接続するために使用される開発ツールです。率直に言って、他の言語が呼び出すためのインターフェイスコードを生成api
できます。次に、プロジェクトの場合、アプリケーション開発の学生がコードを手動でパッケージ化する必要がないため(他の人がコードを作成したため)、パブリックプロジェクトパッケージに対応するコードをGo
直接生成できるため、アクセスのコストが削減されます。パーティ。C/C++
cgo
Go
cgo
またswig
、言語用に設計されているだけでなく、、、などのGo
多くの高級言語に適合しているため、対応する言語によって呼び出されるインターフェイスを生成できます。これは、アプリケーション層がすばやくアクセスするのに便利です。Python
Java
PHP
Javascript
この記事は主に紹介swig
とGo
言語統合に関するものです。他の言語にも興味がある場合は、公式ドキュメントを確認することをお勧めします。
実際のアプリケーションでは、それが大規模なC ++プロジェクトである場合、swigの使用はさらに強力になります。ただし、小規模なCプロジェクトの場合、swigを使用すると事態が複雑になる可能性があるため、ケースバイケースで評価することをお勧めします。
swigをインストールします
linux
インストールはソースコードから直接ダウンロードしてコンパイルできます:www.swig.org/download.ht…もパッケージをyum
提供swig
しますが、デフォルトバージョンは比較的古いmacOS
ため、直接使用しbrew install swig
て。他のプラットフォームを選択する必要がある場合は、公式ドキュメントを確認できます:swig install
wget http://prdownloads.sourceforge.net/swig/swig-{versoin}.tar.gz && tar -zxvf
cd swig-*
./configure
make
sudo make install
复制代码
スウィッグプロセスを使用する
使用法swig
はそれほど複雑ではなく、主に2つのステップに分かれています。
- 定义
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
cgo
コード例の呼び出し: github.com/swig/swig/t…swig
cgo
ドキュメントの呼び出し: Goを使用したSWIGの実行。