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

前言

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

总结:

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

(0)

相关推荐

  • C/C++在Java、Android和Objective-C三大平台下实现混合编程

    Android和iOS开发都支持C++开发,可以一套代码多平台使用.同时C++难以反编译的特性也可以为Android开发带来代码的保密,另一native特性也可以提高代码的运行效率. 一.为什么使用C/C++ 便于移植,用C/C++写得库可以方便在其他的平台上再次使用. 代码的保护,由于java层代码很容易被反编译,而C/C++库反汇难度较大. 提高程序的执行效率,将要求高性能的应用逻辑使用C/C++开发,从而提高应用程序的执行效率. 访问现有开源库,需要访问底层的API或引用一些只有C/C++

  • python和C语言混合编程实例

    最近为了测试网速情况怎么样,由于部分业务服务器需要关闭icmp,这样的话采用普通的ping就无法适应我的需求,于是自己简单的写了一个基于tcp端口的ping的程序,由于c执行效率比较的不错,但是开发效率低下,而python是开发效率高,但是执行效率不如C,由于需要大规模的使用,于是用C实现核心部分的代码,并把这部分实现成一个python的模块,由python调用c的模块,下面就贴代码吧 复制代码 代码如下: /* tcpportping.c */#include <Python.h>#incl

  • 如何利用Boost.Python实现Python C/C++混合编程详解

    前言 学习中如果碰到问题,参考官网例子: D:\boost_1_61_0\libs\python\test 参考:Boost.Python 中英文文档. 利用Boost.Python实现Python C/C++混合编程 关于python与C++混合编程,事实上有两个部分 extending 所谓python 程序中调用c/c++代码, 其实是先处理c++代码, 预先生成的动态链接库, 如example.so, 而在python代码中import example;即可使用c/c++的函数 . em

  • 深入理解C/C++混合编程

    在工作中,C.C++密不可分,做我们嵌入式方面的,当然更多的是C,但,有时候却少不了C++,而且是C.C++混搭(混合编程)在一起的,比如,RTP视频传输,live555多媒体播放等都是C++下的,他需要调用JRTPLIB库,再比如,我那邮件发送,我也用C++写的,定义了一个Email对象,包含了成员:收发邮件地址,用户名,密码等,以及方法:邮件头.Base64编码和邮件发送这些操作,很好用,所以,很多时候,C++还是蛮不错的....但,*.c与*.cpp文件混搭在一起,不是那么的简单,知识总是

  • 批处理与python代码混合编程的方法

    批处理可以很方便地和其它各种语言混合编程,除了好玩,还有相当的实用价值,比如windows版的ruby gem包管理器就是运用了批处理和ruby的混合编写,bathome出品的命令工具包管理器bcn 使用了bat+jscript的混编实现的. cn-dos和bathome论坛里先后有帖子介绍和示范了批处理和各种语言脚本的混合编程,有兴趣可以搜索看看. python不挑剔文件后缀,只要程序中包含正确的python代码都可以用python 解释器解释执行. 批处理与python的混合编程方法很简单,

  • C和C++混合编程问题

    分析以下一段代码: /*=======sum.h=========*/ #ifndef SUM_H #define SUM_H #include <stdio.h> int sum(int a,int b); #endif; /*=======sum.c=========*/ #include "sum.h" int sum(int a,int b) { int c=a+b; return c; } /*====main.cpp======*/ #include "

  • linux下编译boost.python简单方法

    最近项目使用c++操作Python脚本,选用boost.python库.在window下编译安装很顺利,但是在Linux下一直编译不通过,总是提示找不到头文件.linux版本为rhel5.7.后来询问同事,原来是同事将原来系统自带的python2.4删除掉了,然后手动编译安装了python3.3. 换到另外一台机器,重新下载boost,使用以下命令,顺利编译成功 ./bootstrap.sh --with-python=/usr/bin/python ./bjam --build-type=mi

  • C/C++混合编程之extern “C”的使用示例

    前言 本文主要给大家介绍了关于C/C++混合编程extern "C"使用的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 首先要明白: C++号称是C语言的超集,也确实,从语言的基本语法上,C++是包含所有C语言的语法的,而且C++为了兼容C,连C语言的标准库也被纳入到C++的标准库中,比如在C++中我们仍然可以使用<stdio.h> ,它就是C++标准库的一部分(注意最好用新的标准<cstdio> ,而不是老的<stdio&g

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

    前言 最近开始使用 robot framework 测试c++的动态库,robot framework 是跑在 windows 上面,c++动态库是跑在远程linux主机上面.测试办法是让 robot framework 通过 SSHLIbrary 库执行远程机器上面的 python 脚本,python 脚本调用 C++ 动态库.所以现在要解决的是如何让python调用c++动态库. python调用c++动态库的两种办法 在上网查资料和咨询同事之后,得到两种办法:第一种将C++动态库封装成C接

  • 关于python调用c++动态库dll时的参数传递问题

    目录 string cv::Mat string C++生成dll代码: #include <iostream> extern "C" __declspec(dllexport) int get_str_length(char *str); int get_str_length(char *in_str) { std::string str(in_str); return str.length(); } 将VS_create_dll.dll放在与python相同文件夹下.p

  • Python调用.net动态库实现过程解析

    pythonnet简介 pythonnet是cpython的扩展 pythonnet提供了cpython和.net程序集之间交互的桥梁 pythonnet开源在github上 pythonnet安装 通过pip install pythonnet安装 pythonnet的使用帮助 pythonnet的使用帮助,请参见github. pythonnet中的坑 cpython是分32和64位的,对应的pythonnet也是分的,版本要对应好 pythonnet最核心的就是python.Runtime

  • Linux下g++编译与使用静态库和动态库的方法

    在windows环境下,我们通常在IDE如VS的工程中开发C++项目,对于生成和使用静态库(*.lib)与动态库(*.dll)可能都已经比较熟悉,但是,在linux环境下,则是另一套模式,对应的静态库(*.a)与动态库(*.so)的生成与使用方式是不同的.刚开始可能会不适应,但是用多了应该会习惯这种使用,因为步骤上并没有VS下配置那么繁琐. 下面就分别总结下linux下生成并使用静态库与动态库的方法:(由于是C++项目,所以编译器用的g++,但是与gcc的使用是相通的) 首先是准备工作,把我们需

  • python调用xlsxwriter创建xlsx的方法

    详细的官方文档可见:http://xlsxwriter.readthedocs.io/ 通过pip安装xlsxwriter pip install xlsxwriter 下面进行基本的操作演示: 1. 首先创建一个excel的文档 workbook = xlsxwriter.Workbook(dir) 2. 在文档中创建表 table_name = 'sheet1' worksheet = workbook.add_worksheet(table_name) # 创建一个表名为'sheet1'的

  • python调用staf自动化框架的方法

    1.配置环境 支持python2和python3 On Linux, Solaris, or FreeBSD, add the /usr/local/staf/lib directory to your PYTHONPATH, assuming you installed STAF to directory /usr/local/staf. For example: export PYTHONPATH=/usr/local/staf/lib:$PYTHONPATH On Mac OS X, ad

  • 在 Linux/Mac 下为Python函数添加超时时间的方法

    我们在使用 requests 这类网络请求第三方库时,可以看到它有一个参数叫做 timeout ,就是指在网络请求发出开始计算,如果超过 timeout 还没有收到返回,就抛出超时异常.(当然存在特殊情况timeout 会失效,请看Timeouts and cancellation for humans* 这篇文章中作者的举例,我们不考虑这种特殊情况). 但大家有没有考虑过,如何为普通的函数设置超时时间?特别是在运行一些数据处理.AI 相关的代码时,某个函数可能会运行很长时间,我们想实现,在函数

  • C#调用C++动态库接口函数和回调函数方法

    目录 1. 前言 2. 普通接口函数调用示例 2.1 C++端编写接口 2.2 C#端调用 3. 回调函数调用示例 3.1 C++端编写接口 3.2 C#端调用 1. 前言 需求: 当前C已经写好了一个动态库,完成了产品开发需求,C#需要调用C编写的动态库DLL接口,开发出完整的软件,DLL动态库里包含了普通接口函数,回调函数. 开发环境: win10 64位 .VS2017 2. 普通接口函数调用示例 2.1 C++端编写接口 (1)头文件里声明需要提供的接口,导出接口,方便C#调用 //带返

  • python 调用win32pai 操作cmd的方法

    实例如下: #coding=utf-8 import subprocess from time import * import win32api import win32con import win32gui subprocess.Popen('C:\windows\system32\cmd.exe') sleep(1) a=65;b=66;c=67;d=68;e=69;f=70;g=71;h=72;i=73;j=74;k=75 l=76;m=77;n=78;o=79;p=80;q=81;r=8

随机推荐