linux中使用boost.python调用c++动态库的方法
作者:问天小凯 发布时间:2023-01-19 19:21:03
前言
最近开始使用 robot framework 测试c++的动态库,robot framework 是跑在 windows 上面,c++动态库是跑在远程linux主机上面。测试办法是让 robot framework 通过 SSHLIbrary 库执行远程机器上面的 python 脚本,python 脚本调用 C++ 动态库。所以现在要解决的是如何让python调用c++动态库。
python调用c++动态库的两种办法
在上网查资料和咨询同事之后,得到两种办法:第一种将C++动态库封装成C接口,让python调用C语言接口。由于python只能调用C接口,无法直接调用C++接口,所以需要一层封装。封装办法:使用extern “C”声明方式,在C++的接口之上,封装一层C语言接口。这种办法经过尝试,发现纯C调用可行,但是python调用不可行,下面会具体讲解原因。第二种办法是使用c++的boost库,生成供python调用的接口,经测试可行,但是过程很曲折,下文会具体讲解遇到的问题和解决办法。
理解extern “C”的本质
在讲述第一种办法之前,先简单介绍一下extern “C”方式的作用。具体讲解可以参考这篇博客,讲的很详细,推荐阅读。举一个例子,在c语言中,有一个函数
int add(int a,int b);
如果使用gcc编译器,那么编译生成的名字就叫 add,但是如果使用 g++编译器,可能编译生成的名字就叫类似于ABaddCD这样的,这里面包含了函数名、入参个数、类型、返回值。所以,如果c++中还定义了一个重载的
float add(float a,float b);
可能编译生成的名字就叫类似于 EFaddGH这样的,同样包含了函数名、入参、返回值等信息,所以c++可以重载。试想一下,如果使用gcc编译器,那么都叫add,就无法区分哪个函数了,所以无法重载。那么,extern “C”的作用,就是告诉g++编译器,将 int add(int a,int b)编译成 add,而不是编译成 ABaddCD,因为 add 才能被 C 语言识别,ABaddCD无法被C语言识别,C语言会认为 add是'undefined symbol'。所以,从这里我们也可以看出,extern “C”只能用于 c++代码,另外,对于存在重载的c++函数,需要写两个不一样的函数分别调用,保证名字不重复。
python使用extern “C”方式调用c++动态库
知道了extern “C”的本质之后,我们就按照这个方法进行封装。我是直接拿着c++动态库的源码,在源码之上封装一层C接口,然后生成动态库。假设将 add 函数封装成 addc,C++动态库叫做A,封装一层C接口之后生成的动态库叫做B。如果写一个test.c的测试代码,使用纯C代码检验动态库B,调用addc函数,结果是可行的,成功的。但是使用python检验动态库B,调用addc函数,发现会报这样的错误:
AttributeError: B.so: undefined symbol: add
就是说依然不识别add函数。使用
nm B.so | grep add
能够得到
addc
ABaddCD
这样的结果,第一个 addc 肯定是可以被 python 识别的,第二个 ABaddCD,是g++编译生成的名字,无法被python调用。我这只是举一个自己的例子,我自己的C++动态库的源码可能写的比较复杂,无法被python 成功调用,网上还有很多例子,说的是可以被成功调用。所以读者可以自行试验,如果能够成功调用,自然是最好的。因为接下来要介绍的使用 boost.python的方式比较曲折。
python使用 boost.python 调用c++动态库
解决c++动态库依赖的其他的第三方库
由于我的动态库依赖了其他第三方的库文件,比如openssl,uuid,libevent,pthread,所以,不管是使用哪种办法调用c++动态库,都需要python加载这些动态库,具体python代码如下:
from ctypes import *
ctypes.CDLL("libssl.so", mode=ctypes.RTLD_GLOBAL)
ctypes.CDLL("libcrypto.so", mode=ctypes.RTLD_GLOBAL)
ctypes.CDLL("libuuid.so", mode=ctypes.RTLD_GLOBAL)
ctypes.CDLL("/usr/lib64/libevent.so", mode=ctypes.RTLD_GLOBAL)
#ctypes.CDLL("/usr/lib64/libpthread.so.0", mode=ctypes.RTLD_GLOBAL)
有一些可以默认加载,比如 libpthread.so,我们不需要加载,其他的则需要手动加载,像 libssl.so,libuuid.so,都在 /usr/lib64/目录下,可以不加路径,但是libevent库也是/usr/lib64目录下,且在 /usr/lib/目录下也有,又必须加路径。所以,如果编译不通过,就使用 whereis libevent.so 查看在哪个目录,然后加上绝对路径。有时候加上绝对路径依然不对,比如libpthread.so,加上绝对路径之后还是报错
'OSError: /usr/lib64/libpthread.so: invalid ELF header'
这意味着版本号不对,找到 libpthread.so 链接的版本号,加上 .0 版本号,则不会报错。
c++代码配置boost环境
在c++动态库所在的centos6.6机器上面,我参考: ubuntu下python调用C/C++方法之动态链接库配置和试验boost。参考:利用Boost.Python实现Python C/C++混合编程实现python定义c++的函数重载。配置环境时,我使用的命令是:yum install boost*, yum install python-devel,参考这两篇文章实现boost,基本上都能通过,遇到的问题,里面也有。另外我也遇到其他问题,在Stack Overflow上面找到解决办法,我下面就直接贴一下结果:
新建一个 test.cpp,在这个cpp里面我们要定义 python可用的函数。
在 test.cpp 代码中,包含以下代码:
// 需要包含boost的头文件
#include <boost/python.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
//重载函数的实现,在我的 c++代码中,LOGIN 函数、Synchronize_Request函数、Notify函数都有三个重载函数,下面我只用到了其中一个LOGIN函数,一个Synchronize_Request函数,2个Notify函数,比如下面的fun3和fun4,就是两个不同的notify。
//只有存在重载的函数才需要像这样定义 fun1,fun2,fun3,fun4,不存在重载的函数,可以直接写名字
int (*fun1)(const int server_type, const int request_no, std::string& login_result) = &LOGIN;
int (*fun2)(const int server_type, const int request_no,std::string& recv_answer) = &Synchronize_Request;
int (*fun3)(const int server_type, unsigned int timeout_ms, unsigned int sesscare ) = &Notify;
int (*fun4)(void) = &Notify;
// add 函数重载举例
int (*fun5)(int a,int b) = &add;
BOOST_PYTHON_MODULE( libB ) //python模块,libB的名字要与 .so 的名字一致
{
using namespace boost::python;
//Initialize 函数没有重载,直接使用即可,不需要像上面一样定义出 fun1
def("Initialize",Initialize);
//Uninitialize 函数没有重载,直接使用即可
def("Uninitialize",Uninitialize);
def("LOGIN",fun1);
def("Synchronize_Request",fun2);
def("Notify",fun3);
def("Notify2",fun4);
def("add",fun5);
// python 可以调用以上def定义的函数
}
Makefile 使用的命令是:
%.o : %.cpp
g++ -g -lssl -fPIC -levent -lcrypto -luuid -lpthread -lrt -lboost\_filesystem -lboost\_system -lboost_python -lpython -I/usr/include/python2.7 -o $@ -c $<
生成B.so的命令是:
g++ -shared -Wl,-soname,libB.so -o libB.so *.o -lpython -lboost_python
python脚本中则需要引入该动态库
import libB
print libB.add(10,20)
按照上面的命令进行编写、编译,就能规避我踩过的坑。注意 -lpython 的位置,不要放在前面。 如果没有实现重载的定义,而是直接使用 def("LOGIN",LOGIN); 则会报如下的错误 error: no matching function for call to ‘def(const char [15], <unresolved overloaded function type>)' def("LOGIN",LOGIN); 综上是我花了一整天时间研究的成果,如有错漏,还请读者指出,谢谢。
补充:当采用boost.python的方式调用c++动态库的时候,我无法处理引用类型,比如 string& recv_answer 用来接收返回结果,被识别为 string{lvalue},而我的python传入的是 string 类型,无法匹配。所以我就手动将 string& recv_answer的string类型的引用,改写成 char * recv_answer_c 格式,就是改成 C 语言的风格,然后用下面的方式传入 recv_answer_c 这个参数用来接收结果。
#采用 bytes 的方式,为变量预先分配空间,保证不会段错误
temp = bytearray(1000)
recv_answer_c= bytes(temp)
来源:https://my.oschina.net/u/2447371/blog/1920682


猜你喜欢
- 1、问题在使用Python中pandas读取csv文件时,由于文件编码格式出现以下问题:Traceback (most recent cal
- '数据库连接 Public Sub connectionDB() Try serverUrl = readFromIni(My.Ap
- VScode编辑器在安装好Python插件之后会自动选择环境变量中排序最高的那一个解释器作为默认解释器,而想要额外添加新的Python解释器
- Python函数用法和底层分析函数是可重用的程序代码块。函数的作用,不仅可以实现代码的复用,更能实现代码的一致性。一致性指的是,只要修改函数
- xbox series和ps5发售以来,国内黄牛价格一直居高不下。虽然海外amazon上ps5补货很少而且基本撑不过一分钟,但是xbox s
- binlog介绍binlog,即二进制日志,它记录了数据库上的所有改变.改变数据库的SQL语句执行结束时,将在binlog的末尾写入一条记录
- 最近做了一个项目其中有项目需求涉及到访问控制,在访问需要登录才能使用的页面或功能时,会弹出登录框:效果如下: 图 1-点击用户名时,如未登录
- 本文实例为大家分享了Python实现学生成绩管理系统的具体代码,供大家参考,具体内容如下基本功能:输入并存储学生的信息:通过输入学生的学号、
- 1、向List中添加元素的方法1.1 Python append()方法添加元素append() 方法用于在列表的末尾追加元素,该方法的语法
- 前言H2数据库是一个开源的关系型数据库。H2采用java语言编写,不受平台的限制,同时支持网络版和嵌入式版本,有比较好的兼容性,支持相当标准
- match()函数只检测RE是不是在string的开始位置匹配, search()会扫描整个string查找匹配, 也就是说match()只
- 简单介绍下SecureCRTSecureCRT是一款支持SSH(SSH1和SSH2)的终端仿真程序,简单地说是Windows下登录UNIX或
- 一条记录可以看做一条数据的数组1 Html1.1 批量选择框1.2&
- 存储过程和函数是在数据库中定义一些SQL语句的集合,然后直接调用这些存储过程和函数来执行已经定义好的SQL语句。存储过程和函数可以避免开发人
- 在很多情况下,我们可能需要控制某一段代码只执行一次,比如做某些初始化操作,如初始化数据库连接等。 对于这种场景,go 为我们提供了 sync
- 在官网下载源码包:https://www.php.net/downloads.php步骤:1、解压命令:tar -xjvf php.tar.
- 摘要PIL.Image.open读入的是RGB顺序,而opencv中cv2.imread读入的是BGR通道顺序 。cv2.imread会显示
- 关于 *args与**args的用法*args 和 **kwargs主要用于函数定义,你可以将不定数量的参数传递给某个函数。*args*ar
- Python之成为图像处理任务的最佳选择,是因为这一科学编程语言日益普及,并且其自身免费提供许多最先进的图像处理工具。本文主要介绍了一些简单
- 快到 520 了,分享几段 520 专属 Python 代码,不多说了,下面直接上货。No.1效果:主要代码:import tur