• 售前

  • 售后

热门帖子
入门百科

linux中利用boost.python调用c++动态库的方法

[复制链接]
123457294 显示全部楼层 发表于 2021-10-25 19:45:49 |阅读模式 打印 上一主题 下一主题
媒介
近来开始利用 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语言中,有一个函数
  1. int add(int a,int b);
复制代码
如果利用gcc编译器,那么编译天生的名字就叫 add,但是如果利用 g++编译器,可能编译天生的名字就叫类似于ABaddCD这样的,这内里包含了函数名、入参个数、范例、返回值。以是,如果c++中还定义了一个重载的
  1. 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函数,发现会报这样的错误:
  1. AttributeError: B.so: undefined symbol: add
复制代码
就是说依然不识别add函数。利用
  1. nm B.so | grep add
复制代码
能够得到
  1. addc
  2. ABaddCD
复制代码
这样的结果,第一个 addc 肯定是可以被 python 识别的,第二个 ABaddCD,是g++编译天生的名字,无法被python调用。我这只是举一个本身的例子,我本身的C++动态库的源码可能写的比较复杂,无法被python 成功调用,网上另有很多例子,说的是可以被成功调用。以是读者可以自行试验,如果能够成功调用,自然是最好的。由于接下来要先容的利用 boost.python的方式比较曲折。
python利用 boost.python 调用c++动态库

办理c++动态库依靠的其他的第三方库

由于我的动态库依靠了其他第三方的库文件,比如openssl,uuid,libevent,pthread,以是,不管是利用哪种办法调用c++动态库,都需要python加载这些动态库,详细python代码如下:
  1. from ctypes import *
  2. ctypes.CDLL("libssl.so", mode=ctypes.RTLD_GLOBAL)
  3. ctypes.CDLL("libcrypto.so", mode=ctypes.RTLD_GLOBAL)
  4. ctypes.CDLL("libuuid.so", mode=ctypes.RTLD_GLOBAL)
  5. ctypes.CDLL("/usr/lib64/libevent.so", mode=ctypes.RTLD_GLOBAL)
  6. #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,加上绝对路径之后照旧报错
  1. '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 代码中,包含以下代码:
  1. // 需要包含boost的头文件
  2. #include <boost/python.hpp>
  3. #include <boost/python/module.hpp>
  4. #include <boost/python/def.hpp>
  5. //重载函数的实现,在我的 c++代码中,LOGIN 函数、Synchronize_Request函数、Notify函数都有三个重载函数,下面我只用到了其中一个LOGIN函数,一个Synchronize_Request函数,2个Notify函数,比如下面的fun3和fun4,就是两个不同的notify。
  6. //只有存在重载的函数才需要像这样定义 fun1,fun2,fun3,fun4,不存在重载的函数,可以直接写名字
  7. int (*fun1)(const int server_type, const int request_no, std::string& login_result) = &LOGIN;
  8. int (*fun2)(const int server_type, const int request_no,std::string& recv_answer) = &Synchronize_Request;
  9. int (*fun3)(const int server_type, unsigned int timeout_ms, unsigned int sesscare ) = &Notify;
  10. int (*fun4)(void) = &Notify;
  11. // add 函数重载举例
  12. int (*fun5)(int a,int b) = &add;
  13. BOOST_PYTHON_MODULE( libB ) //python模块,libB的名字要与 .so 的名字一致
  14. {
  15. using namespace boost::python;
  16. //Initialize 函数没有重载,直接使用即可,不需要像上面一样定义出 fun1
  17. def("Initialize",Initialize);
  18. //Uninitialize 函数没有重载,直接使用即可
  19. def("Uninitialize",Uninitialize);
  20. def("LOGIN",fun1);
  21. def("Synchronize_Request",fun2);
  22. def("Notify",fun3);
  23. def("Notify2",fun4);
  24. def("add",fun5);
  25. // python 可以调用以上def定义的函数
  26. }
复制代码
Makefile 利用的下令是:
  1. %.o : %.cpp
  2. 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的下令是:
  1. g++ -shared -Wl,-soname,libB.so -o libB.so *.o -lpython -lboost_python
复制代码
python脚本中则需要引入该动态库
  1. import libB
  2. 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 这个参数用来接收结果。
  1. #采用 bytes 的方式,为变量预先分配空间,保证不会段错误
  2. temp = bytearray(1000)
  3. recv_answer_c= bytes(temp)
复制代码
总结:
以上就是这篇文章的全部内容了,盼望本文的内容对各人的学习大概工作具有肯定的参考学习价值,如果有疑问各人可以留言交换,谢谢各人对草根技术分享的支持。

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作