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++库。

Swig 的安装过程较简单,可以参考此博客https://blog.csdn.net/veryitman/article/details/17398151

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 工具的格式和语法可以进行网上学习,给出参考博客:

http://www.cnblogs.com/kaituorensheng/p/4464117.html

下面给我使用这个工具的注意事项:

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 编写和测试)。

找不到动态库的解决:

解决Linux系统下程序找不到动态库的方法

方法一:修改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   使得修改的路径生效

https://www.cnblogs.com/xudong-bupt/p/3698294.html

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 的官方文件

猜你喜欢

转载自blog.csdn.net/jishuqianjin/article/details/83105398