CGO Swig 使用筆記
官網: https://www.swig.org/
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
為 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;