前言

最近开始使用 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

#include

#include

//重载函数的实现,在我的 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], )' 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)

总结:

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

python调用c++动态库 linux_linux中使用boost.python调用c++动态库的方法相关推荐

  1. python读取idx_通过Python从.idx3-ubyte文件或GZIP中提取图像 - python

    我已经使用OpenCV中的facerecognizer创建了一个简单的人脸识别功能.它可以很好地处理人的图像. 现在,我想通过使用手写字符而不是人来进行测试.我遇到了MNIST数据集,但它们将图像存储 ...

  2. 在OS X 中安装boost.python, 使用homebrew[译]

    <embed src="//music.163.com/style/swf/widget.swf?sid=466844094&type=2&auto=1&wid ...

  3. python对笔记本电脑的要求-笔记本电脑中多版本python的配置

    笔记本电脑中多版本python的配置 在我的笔记本中,已经安装了两个版本的python: python2.7.11 python3.5.0b2 因为首先安装了python3,因此系统的环境配置中保留了 ...

  4. python脚本自动运行失败_Linux中定时任务执行python脚本失败的解决方法

    原博文 2019-03-25 01:48 − 因工作需要,需要在服务器中写一个定时任务,定时把redis数据库中的数据查询出来存放在mysql数据库中,奈何没写过shell脚本,只能用python代替 ...

  5. @async方法不调用了_在Spring中使用Future对象调用Async方法调用

    @async方法不调用了 下一个示例将演示Spring容器内部的异步方法调用. 为什么我们需要异步方法调用? 在某些情况下,我们并不真正知道是否需要重播或何时应返回结果. 传统方式在Java EE世界 ...

  6. 内部服务并行调用_25行以下代码中的并行SOAP调用的Express服务

    内部服务并行调用 总览 (Overview) Let's suppose there is a service that has the following features: 假设有一项服务具有以下 ...

  7. python输入文字垂直输出_python中len用法-python计算数学表达式-利用python如何垂直输出文字...

    python中len是什幺意思 >>> list = [1,2,3] >>> len(list) 3 >>> l = '123' >> ...

  8. 不属于python语言保留字_以下选项中,不属于Python保留字的是( )_学小易找答案...

    [判断题]用摆式仪测试摆值同一处平行测定不少于3次,3个测点间距2~5m.( ) [单选题]下面是单拍子的是 [填空题]假设列表对象 aList 的值为 [3,4,5,6,7,9,11,13,15,1 ...

  9. vs如何把c语言编译成静态库,VS中Debug和Realease、及静态库和动态库的区别整理(转)...

    一.Debug和Realease区别产生的原因 Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序.Release 称为发布版本,它往往是进行了各种优化,使得程序在代码 ...

最新文章

  1. jQuery EasyUI DataGrid 分页 FOR ASP.NET
  2. 数据结构与算法实验祝恩_《数据结构与算法》实验教学大纲
  3. android 调用百度sdk后运行不了,解决android程序中oncreate方法中调用百度地图MKSearchListener接口不执行的问题...
  4. 002 Servlet 第一个程序
  5. 类的变量初始化是直接使用初始化器还是放在构造函数中?
  6. Javascript ES6 Promise异步链式读取文件解决回调地狱
  7. bilibili弹幕转ass
  8. [WPF系列]-DataBinding(数据绑定) 自定义Binding
  9. 高德地拖缩放怎么保证中心点不变_出国学习,价值何在?听听留学生们怎么说...
  10. 北京无人驾驶路测进入“主驾无人”阶段
  11. idea修改主题后,重新设置字体大小
  12. 教案用计算器计算机,用计算器计算教案
  13. 如何用计算机设计衣服,如何用电脑设计服装
  14. Zookeeper -选举流程
  15. java happen-before_Java happen-before规则
  16. Mysql最新版8.0.21下载安装配置教程
  17. EndNote导入文献引用后无法显示期刊名称的解决办法
  18. Nginx 反向代理、负载均衡、虚拟主机实践教程
  19. Red Herring2010年亚洲区创新实用产品排名
  20. 微赞微擎提示:警告!您使用的插件不是官方安装的,请移步论坛,解决办法

热门文章

  1. Python 频繁读取Mysql相关问题
  2. Cocos2d-X数据、动作、消息的基本操作
  3. 自定义Button防止重复提交
  4. MATLAB与Python numpy矩阵操作对应表
  5. Python中排序函数sorted和排序方法sort的异同点对比分析
  6. 【经典算法必读】图片分类系列之(一): 你真的了解图片分类(Image Classification)吗?...
  7. Python中 sys.argv[]的用法简明解释
  8. Windows10 Postgres11 安装 The Database cluster initialisation failed
  9. [BZOJ 2756] 奇怪的游戏
  10. 动手动脑——登录界面