CGO Swig 使用筆記

花狸狸發表於2024-08-16

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++ 類生成一對 NewXxxxDeleteXxxx 的建立刪除介面(脫離 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;

相關文章