1. 程式人生 > >swig 將c++轉換為python 介面

swig 將c++轉換為python 介面

為了能將scanlib和python程式碼無縫連線,我們需要通過python來呼叫scanlib的介面。

一、 利用swig將scanlib的c++版轉換為python 版

1.1 swig 的說明以及安裝

SWIG(Simplified Wrapper and Interface Generator)是一個為C/C++庫提供指令碼呼叫支援的工具,簡單的來說就是將c/c++包裝成其他程式的介面,通過其介面呼叫c/c++庫。

1.2 編寫swig 的介面檔案

這個實現c++轉換python 的關鍵檔案,此文件的編寫格式,語法官網有詳細的教程。下面給出我轉換scanlib的介面檔案並作相對應的解釋。

%module scanapi 表示生成的模組名為scanapi %include <std_string.i> %include std_string.i 主要用於處理string類,const string &,特別注意不能處理string & 因為在這種情況下它和指標當作同一種東西處理 %include typemaps.i 通常用於更改引數轉換的某些屬性 %include cpointer.i  The cpointer.i module defines macros that can be used to used to generate wrappers around simple C pointers.

%{ #define SWIG_FILE_WITH_INIT #include "scanapi.h" #include "commonapi.h" %} 將要轉換的標頭檔案新增進來即可

%include "commonapi.h" %include "scanapi.h" 暴露要轉換的介面,生成模組後可以看到這些介面,可以寫函式也可以include 標頭檔案。

%pointer_functions(int, intP) %pointer_functions(std::string,stringP) 這個是結合%include cpointer.i 用來生成五個函式,如下:

new_name() 生成對應型別的指標,會進行記憶體的分配。 copy_name(value) 生成對應型別的指標,指向分配的記憶體並進行賦值 Delete_name(obj) 對記憶體進行釋放 Name_assign 對指標進行賦值操作 Name_value 獲取指標的值 以上五個函式在轉換完之後可以在擴充套件模組中找到,我採用這個主要是處理string & 問題,這個在後面進行說明。

1.3 利用distutils工具構建編譯過程生成動態庫

這個是編譯過程中的關鍵所在,需要詳細的瞭解。Distutils 工具的格式和語法可以進行網上學習,給出參考部落格:

下面給我使用這個工具的注意事項:

extra_compile_args 用來新增g++/gcc 的編譯選項

include_dirs   新增標頭檔案目錄

library_dirs    新增庫目錄

Libraries      新增庫的名稱

有了以上四個就可以解決編譯時的大部分問題

ext_modules 要和Extension 中的一致

1.4 編譯過程

① swig -python scanapi.i gcc 編譯會生成c檔案的包裝檔案 swig -python -c++ scanapi.i 使用g++ 編譯會生成cxx 型別的包裝檔案 ② python3.5 setup.py build 不出錯則會在build/lib*目錄下生成python 擴充套件模組 ③ Python3.5 setup.py install 將生成的scanlib的擴充套件模組進行安裝 其中上面python3.5 是我採用的3.5 版本的python,各位操作的時候可以改為相應的版本。到此我已經將c++ 版本的scanlib庫轉化成了python 版本,是不是很神奇?接下來我編寫libtest 的python版本進行測試(在pycharm 編寫和測試)。

找不到動態庫的解決:

方法一:修改LD_LIBRARY_PATH環境變數

特點:這種方法主要處理臨時的動態庫載入,LD_LIBRARY_PATH環境變數修改後,只能是對當前的使用者生效。

LD_LIBRARY_PATH的作用:這個環境變數用於在程式載入執行期間查詢動態連結庫時指定除了系統預設路徑之外的其他路徑,注意,LD_LIBRARY_PATH中指定的路徑會在系統預設路徑之前進行查詢。

具體步驟:

1、找到動態庫所在路徑:sudo find / -name "[動態庫名字]"

2、開啟~/.bashrc檔案

3、在該檔案最後一趟新增:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:[動態庫所在的絕對路徑]

4、source ~/.bashrc

二、通過pycharm 編寫libtest 測試scanlib python 版本

2.1  在pycharm 中新增scanapi擴充套件模組的路徑

新增的過程如下

File -> Settings

點選漏斗下面的那個

點選+將你上面安裝的scanapi 模組的路徑新增進去,最後一行是我安裝scanapi 的路徑,然後點選ok

到此新增擴充套件模組路徑完成。

2.2 編寫libtest 的python 版本的程式碼

Import scanapi 然後使用生成的介面編寫實現libtest

測試的結果如下:

因此驗證此次轉換是起作用的,成功的。

三、過程出現的問題及感想

3.1 出現的問題

1) 當c++介面是非const 引用的時的處理方法,出現如下問題:

總是出現型別不對。

由於scanlib 版本的程式碼已經經過了測試並投入的實際的使用,如果將std::string & 改成 std::string 以及 const std::string & 引用(後兩者都有解決辦法),多個函式都是這樣寫的改起來風險比較大,怎麼辦呢?

谷歌和百度很久得不到解決辦法,在官方文件上有這麼一句話就是swig 把非const 引用和指標當作同一個東西看待,那也就是說我解決了指標問題引用也就解決了。最後通過如下方法解決:

處理的過程是:

① 在介面檔案中新增

   %include cpointer.i

%pointer_functions(std::string,stringP)

② 然後在使用的時候利用

  b=scanapi.new_stringP() 建立一個SwigPyObject 型別的物件

  進行賦值操作

  scanapi.stringP_assign(b,"hello_lcw")

  然後在呼叫

scanapi.SaveToJSON(b)

就可以解決這個問題了

使用完之後要使用 delete_name進行釋放記憶體哦!!!!!

2)找不到scanlib 庫 在檔案/etc/ld.so.conf 末尾新增 scanlib庫的路徑 /sbin/ldconfig 使的上述路徑生效。

3.2 感想

剛開始接觸這個c++ 庫轉python 時,很迷茫不知所錯,網上搜了很多方案有ctypes, cython, capi,sip 以及swig 等,嘗試過ctypes ,cython,capi ,sip 和swig ,其中花了很長一段時間在sip 上,是實現過sip 好幾個小例子雖然說sip是專注於c++/c 轉python 但是使用的人較少出了問題能夠提供的資料不足,最終轉到了swig 工具上,這也告訴我了以後選擇方案的時候要選擇資料多,使用者的多的實現方式這樣能讓自己事半功倍。再者一直就是處理遇到了的問題時一定要多查資料多思考和嘗試,這樣才能找到最優的解決方案,就是上面的非const

引用。

四 專案中出現的問題

4.1

解決方法:

        export C_INCLUDE_PATH=${C_INCLUDE_PATH}:/usr/include/opencv/

        export CPLUS_INCLUDE_PATH=${CPLUS_INCLUDE_PATH}:/usr/include/opencv/

4.2 import 的時候出現沒定義引用的問題

這個很可能是由於scanlib.so 中的某個模組沒有寫cmake_lists ,找到這個檔案新增進去即可。

4.3 在192.168.1.114 訓練機上出現了在xshell 的後臺可以import scanapi 模組,而在pycharm 配置的遠端連線的時候卻顯示找不到libscan.so 動態庫的問題

解決方法:

一:臨時的方法(重啟之後將丟失這個動態庫目錄)

                1、 執行 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:動態庫的目錄

                2、sudo /sbin/ldconfig

二:永久的方法(修改配置檔案)

                1、將.so 檔案路徑的目錄新增到/etc/ld.so.conf

                      sudo vim /etc/ld.so.conf

                      在檔案末尾新增一行,其內容為.so 庫的路徑

                 2、sudo /sbin/ldconfig   使得修改的路徑生效

4.4  svn 配置檔案的問題

train svm failed! OpenCV Error: Assertion failed (mean.data && eigenvectors.data && ((mean.rows == 1 && mean.cols == data.cols) || (mean.cols == 1 && mean.rows == data.rows))) in project, file /build/opencv-SviWsf/opencv-2.4.9.1+dfsg/modules/core/src/matmul.cpp, line 3042 terminate called after throwing an instance of 'cv::Exception' what(): /build/opencv-SviWsf/opencv-2.4.9.1+dfsg/modules/core/src/matmul.cpp:3042: error: (-215) mean.data && eigenvectors.data && ((mean.rows == 1 && mean.cols == data.cols) || (mean.cols == 1 && mean.rows == data.rows)) in function project

bash: line 1: 12612 Aborted (core dumped) env "PYTHONUNBUFFERED"="1" "PYTHONPATH"="/home/chenqq/.pycharm_helpers/pycharm_matplotlib_backend:/tmp/pycharm_project_963" "PYCHARM_HOSTED"="1" "JETBRAINS_REMOTE_RUN"="1" "PYCHARM_MATPLOTLIB_PORT"="21212" "PYTHONIOENCODING"="UTF-8" /home/chenqq/anaconda3/envs/chinese-ocr/bin/python3.6 -u /tmp/pycharm_project_963/service/client.py /media/chenqq/DATA/automodelData/twoPage/LIST.TXT

解決方法:該問題主要是由於配置檔案獨立於libscanlib.so, 需要在測試的執行資料夾下新增配置檔案config.對於pycharm 配置的遠端操作則需要在執行的service 目錄裡以及上一層新增配置檔案即可。

五、SWIG 的官方檔案