CGO Swig 使用笔记
Cgo Swig 指北
Linux 编译 Swig
先下载到 swig 的压缩包解压
必要条件
- Autoconf 2.58 or higher
- Automake 1.7.2 or higher
- A working C and C++ compiler.
- Bison 3.5.0 or higher (to generate the SWIG parser).
- Libpcre (regular expressions library dependency)
通常方法,如果系统中带有足够的编译条件,直接配置安装到系统中
$ tar -xf swig-4.2.0.tar.gz
$ cd swig-4.2.0
$ ./configure
$ make
$ make install
但是在实际工作中,不大可能直接安装到编译服务器中,需要打包到项目里。
安装目录指定
通过 ./configure 的参数 --prefix 指定后续安装的目录
$ ./configure --prefix=/projects_path/bin
$ make
$ make install
编译完成会在 prefix 指向的目录下生成 swig 的可执行文件和代码示例,这些生成文件不能移动,否则会出现路径不对导致的报错。
补充 PCRE2 依赖
可能系统中没有这个依赖,我们需要去官网下载最新的版本,然后放置到 swig 的源码文件夹中,再执行脚本仅给 swig 提供 pcre 的支持,例如:
$ cp ./pcre2-10.44.tar.gz ./swig-4.2.0
$ cd swig-4.2.0
$ Tools/pcre-build.sh
补充 Bison 依赖
系统中可能没有合适的 Bison 版本,我们可以自行编译使用。https://ftp.gnu.org/gnu/bison/
下载合适的 Bison >3.5.0 版本
tar -xf bison-3.8.tar.gz
cd bison-3.8 && ./configure --enable-relocatable --prefix=/bison && make && make install DESTDIR=$HOME/bin
编译完成 Bison 后,在编译 swig 前设置 PATH 变量就好了
为 C/C++ 程序生成 CGO 接口文件
Swig 通过一个后缀为 *.i 的描述文件来生成接口,这个描述文件包含 swig 特殊指令和以及 C/C++ 标准描述语句。
常用参数
%module:每个 SWIG 接口都有一个module参数来描述生成接口的模块名称,在CGO中可以等价于生成的GO文件的package的名称。%{...%}:括号包裹的内容,会被原封不动的拷贝到wrapper文件中,一般用于给包装文件引用源码头文件。%rename (dest_type) source_type:将source_type重命名为dest_type,用来生成转换后的接口,可以用于对不支持的嵌套union结构体等不支持的嵌套类型的重命名接口生成。注意这个%rename参数要位于%include之前,否则不会进行替换当中的内容。%ignore:用于忽略一些函数或者参数的包装转换,可以用于对未实现内容的略过。%include:这个命令用于给 SWIG 引入需要包装转换的头文件,SWIG 会包装转换头文件中包含的函数声明,为 C++ 类生成一对NewXxxx和DeleteXxxx的创建删除接口(脱离 GO 的 GC)等。%feature:用于启用某些 SWIG 特性,例如flatnested。flatnested:针对不支持嵌套的语言,生成非嵌套的内容。
%feature ("flatnested");
%include "xxxx.h"
%feature("flatnested", "");
%insert(cgo_comment):用于直接在 CGO 包装文件中插入注释,例如给 CGO 文件插入链接参数%insert(cgo_comment) %{#cgo LDFLAGS: -L./ -lmylib -ldl%}。%insert(go_wrapper):用于在 CGO 包装文件中插入内容,可以搭配ignore使用。可以对一些被 SWIG 跳过的冲突变量进行补充,由于一些宏定义会被 SWIG 自动替换成变量,但是可能存在冲突,此时就可以根据生成时的冲突提示,手动补充这些变量。或者编写插入一些自定义的补充函数以此集成封装。- 此外还可以编写一些结构体让 SWIG 帮助生成接口,对于无法或不便于整体用
%include导入的头文件,可以利用这个方法部分生成包装方法。
包装命令
- 例如针对 C++ 生成 CGO 包装
$ ./swig -go -gccgo -c++ -intgosize 64 swig_my.i
执行后会在当前目录下自动生成包装文件 xxxx.cxx 和一个 CGO 的接口文件。
建议可以通过 Makefile 等方式将引用到的 C/C++ 头文件拷贝到同一路径中,避免其他项目引用这个包装工程时,编译出现无法找到头文件的情况。
示例
这个示例针对 mylib.h 的头文件做 CGO 的包装,由于带有相同名称的嵌套的结构体,在使用 SWIG 4.2.1 版本时,这些嵌套的结构不会被标记正确的名称,所以在包装文件中,使用了 rename 来重命名符号,让包装器生成正确的 CGO 接口。
mylib.h
// mylib.h
typedef struct My_Struct_1 {
short choise;
union _union {
short choise_1;
short choise_2;
} u;
} My_Struct_1;
typedef struct My_Struct_2 {
short choise;
union _union {
short choise_3;
short choise_4;
} u;
} My_Struct_2;
my_swig.i
%module mylib
%rename (My_Struct_1_union) My_Struct_1::_union;
%rename (My_Struct_2_union) My_Struct_2::_union;
%{
#include "mylib.h"
%}
%feature ("flatnested");
%include "mylib.h"
%feature ("flatnested", "");
%insert(cgo_comment) %{
#cgo LDFLAGS: -L./ -lmylib -ldl
%}
常见包装警告和对应处理
xxxx.h: Warning 325: Nested union not currently supported (_union ignored)
嵌套联合结构不支持,可以对该头文件使用非嵌套特性
# my_swig.i
%feature ("flatnested");
%include "xxxx.h"
%feature("flatnested", "");
xxxx.h: Warning 890: Ignoring 'STRUCT_present' due to Go name ('Present') conflict with 'OTHERSTRUCT_present'
结构体宏定义在包装时,由于包装器命名不完全导致冲突,可以手动插入变量定义解决缺失问题
# my_swig.i
%insert(go_wrapper) %{
const STRUCT_present int = 0x01
const OTHERSTRUCT_present int = 0x02
}
xxxx.h: Warning 890: Ignoring '_union' due to Go name ('X_union') conflict with '_union'
结构体中嵌套匿名结构体时,包装没有生成正确的名称,可以通过 %rename 重命名再让包装器进行包装
%rename (My_Struct_1_union) My_Struct_1::_union;
%rename (My_Struct_2_union) My_Struct_2::_union;
浙公网安备 33010602011771号