python模块与C和C++动态库相互调用实现过程示例

目录
  • Python调用C/C++
    • 1、Python调用C动态链接库
      • C语言文件:pycall.c
      • gcc编译生成动态库libpycall.so
      • Python调用动态库的文件:pycall.py
      • 运行结果:
    • 2、Python调用C/C++原生态导出
    • 3、Python调用C/C++通过boost实现
    • 4、Python调用C/C++通过导出类
    • 5、Python调用C/C++通过导出变参函数
    • 6、Python调用C/C++通过导出带Python对象的接口

Python调用C/C++

1、Python调用C动态链接库

Python调用C库比较简单,不经过任何封装打包成so,再使用python的ctypes调用即可。

C语言文件:pycall.c

# include "stdio.h"
#include "stdlib.h"
int foof(int a,  int b){
    printf("you input %d and %d",a,b);
    return a+b;
}

gcc编译生成动态库libpycall.so

# include "stdio.h"
#include "stdlib.h"
int foof(int a,  int b){
    printf("you input %d and %d",a,b);
    return a+b;
}

Python调用动态库的文件:pycall.py

import ctypes
ll = ctypes.cdll.LoadLibrary
lib = ll("Cfoo.so")
lib.foof(1,3)

运行结果:

2、Python调用C/C++原生态导出

Python解释器就是用C实现,因此只要我们的C++的数据结构能让Python认识,理论上就是可以被直接调用的。我们实现test1.cpp如下:

#include <Python.h>
int Add(int x, int y)
{
    return x + y;
}
int Del(int x, int y)
{
    return x - y;
}
PyObject* WrappAdd(PyObject* self, PyObject* args)
{
    int x, y;
    if (!PyArg_ParseTuple(args, "ii", &x, &y))
    {
        return NULL;
    }
    return Py_BuildValue("i", Add(x, y));
}
PyObject* WrappDel(PyObject* self, PyObject* args)
{
    int x, y;
    if (!PyArg_ParseTuple(args, "ii", &x, &y))
    {
        return NULL;
    }
    return Py_BuildValue("i", Del(x, y));
}
static PyMethodDef test_methods[] = {
    {"Add", WrappAdd, METH_VARARGS, "something"},
    {"Del", WrappDel, METH_VARARGS, "something"},
    {NULL, NULL}
};
extern "C"
void inittest1()
{
    Py_InitModule("test1", test_methods);
}

编译命令如下:

g++ -fPIC -shared test1.cpp -I/usr/include/python2.7 -o test1.so

-fPIC:生成位置无关目标代码,适用于动态连接;
-L path:表示在path目录中搜索库文件,如-L.表示在当前目录;
-I path:表示在path目录中搜索头文件;
-o file:制定输出文件为file;
-shared:生成一个共享库文件;

运行Python解释器,测试如下:

>>> import test1
>>> test1.Add(1,2)
3

这里要注意一下几点:

1.如果生成的动态库名字为test1,则源文件里必须有inittest1这个函数,且Py_InitModule的第一个参数必须是“test1”,否则Python导入模块会失败

2.如果是cpp源文件,inittest1函数必须用extern "C"修饰,如果是c源文件,则不需要。原因是Python解释器在导入库时会寻找initxxx这样的函数,而C和C++对函数符号的编码方式不同,C++在对函数符号进行编码时会考虑函数长度和参数类型,具体可以通过nm test1.so查看函数符号,c++filt工具可通过符号反解出函数原型

3.PyArg_ParseTuple()函数的定义:
arg参数必须是一个元组对象,它包含了从Python传递到C语言函数的参数列表。format参数必须是格式字符串, 具体将在下方解释.其余参数必须是变量的地址,其类型由格式字符串决定。为了能成功的转换,arg对象必须与格式匹配,并且前后一一对应。
虽然 PyArg_ParseTuple() 检查Python是否具有所需类型, 但是它不能检查传递给调用的C变量地址的有效性: 如果在那里出错,您的代码可能会崩溃,或者覆盖内存中的随机位置。所以要小心

字符串 备注
“s” (string or Unicode object) [char *] 将Python字符串或Unicode对象转换为指向字符串的C指针。不能为字符串本身提供存储;指向现有字符串的指针存储在您传递地址的字符指针变量中。C字符串以NULL结尾。Python字符串不能包含嵌入的空字节;如果有,就会引发TypeError异常。使用默认编码将Unicode对象转换为C字符串。如果转换失败,就会引发一个UnicodeError异常
“s#” (string,Unicode or any read buffer compatible object) [char *, int] s”上的这个变体存储在两个C变量中,第一个变量是指向字符串的指针,第二个变量是字符串的长度。在这种情况下,Python字符串可能包含嵌入的空字节。如果可能的话,Unicode对象会返回一个指向对象的默认编码字符串版本的指针。所有其他与读取缓冲区兼容的对象都将对原始内部数据表示的引用传回。
“z” (string or None) [char *] 与“s”类似,但是Python对象也可能是None,在这种情况下,C指针被设置为NULL。
“z#” (string or None or any read buffer compatible object) [char *, int] 这是"s#"就像"z"是"s"一样。
“u” (Unicode object) [Py_UNICODE *] 将Python Unicode对象转换为指向16位Unicode (UTF-16)数据的空端缓冲区的C指针。与“s”一样,不需要为Unicode数据缓冲区提供存储;指向现有Unicode数据的指针被存储到Py_UNICODE指针变量中,您传递的地址就是这个变量。
“u#” (Unicode object) [Py_UNICODE *, int] “u”上的这个变体存储在两个C变量中,第一个变量是指向Unicode数据缓冲区的指针,第二个变量是它的长度。
“es” (string,Unicode object or character buffer compatible object) [const char *encoding,char **buffer] 这个“s”的变体用于编码Unicode和转换为Unicode的对象到字符缓冲区。它只适用于不嵌入空字节的编码数据。

读取一个变体C变量C和商店为两个变量,第一个指针指向一个编码名称字符串(encoding),第二个一个指向指针的指针一个字符缓冲区(**buffer,缓冲用于存储编码数据)和第三个整数指针(*buffer_length,缓冲区长度)。
编码名称必须映射到已注册的编解码器。如果设置为NULL,则使用默认编码。

PyArg_ParseTuple()将使用PyMem_NEW()分配一个所需大小的缓冲区,将已编码的数据复制到这个缓冲区中,并调整*buffer以引用新分配的存储。调用方负责调用PyMem_Free()以在使用后释放分配的缓冲区。
编码名称必须映射到已注册的编解码器。如果设置为NULL,则使用默认编码。操作方式有两种:

如果buffer指向空指针,PyArg_ParseTuple()将使用PyMem_NEW()分配一个所需大小的缓冲区,将已编码的数据复制到这个缓冲区,并调整buffer以引用新分配的存储。调用方负责在使用后调用PyMem_Free()来释放分配的缓冲区。

如果buffer指向非空指针(已经分配的缓冲区),PyArg_ParseTuple()将使用这个位置作为缓冲区,并将buffer_length解释为缓冲区大小。然后,它将把编码后的数据复制到缓冲区中,并终止(0-terminate)它。缓冲区溢出以异常信号表示。

在这两种情况下,都将*buffer_length设置为编码数据的长度,没有后面的0字节(0-byte)。

字符串 备注
“b” (integer) [char] 将Python整数转换为存储在C语言char中的一个小int(tiny int)。
“h” (integer) [short int] 将Python整数转换为C语言short int。
“i” (integer) [int] 将Python整数转换为普通的C语言int。
“l” (integer) [long int] 将Python整数转换为C语言long int。
“c” (string of length 1) [char] 将长度为1的字符串表示的Python字符转换为C语言char。
“f” (float) [float] 将Python浮点数转换为C语言float。
“d” (float) [double] 将Python浮点数转换为C 语言double。
“D” (complex) [Py_complex] 将Python复数转换为C语言Py_complex结构。
“O” (object) [PyObject *] 在C对象指针中存储Python对象(不进行任何转换)。因此,C程序接收传递的实际对象。对象的引用计数没有增加。存储的指针不是空的(NULL)。
“O!” (object)[typeobject, PyObject *] 将Python对象存储在C对象指针中。这类似于“O”,但是接受两个C参数:第一个是Python类型对象的地址,第二个是对象指针存储在其中的C变量(类型为PyObject *)的地址。如果Python对象没有所需的类型,就会引发类型错误(TypeError)。
“O&” (object)[converter,anything] 通过转换器函数将Python对象转换为C变量。这需要两个参数:第一个是函数,第二个是C变量(任意类型)的地址,转换为void *。该转换器功能依次调用如下:status = converter(object,address);对象是要转换的Python对象,地址是传递给PyArg_ConvertTuple()的void *参数。对于成功的转换,返回的状态应该是1,如果转换失败,则返回0。当转换失败时,converter(函数名可能错误)函数应该引发异常。
“S” (string) [PyStringObject *] 与“O”类似,但要求Python对象是字符串对象。如果对象不是字符串对象,则引发类型错误(TypeError)。C变量也可以声明为PyObject *。
“U” (Unicode string) [PyUnicodeObject *] 与“O”类似,但要求Python对象是Unicode对象。如果对象不是Unicode对象,则引发类型错误(TypeError)。C变量也可以声明为PyObject *。
“t#” (read-only character buffer) [char *, int] 与“s#”类似,但接受任何实现只读缓冲区接口的对象。char *变量设置为指向缓冲区的第一个字节,int设置为缓冲区的长度。只接受单段缓冲对象;对所有其他类型都引发类型错误(TypeError)。
“w” (read-write character buffer) [char *] 类似于“s”,但接受任何实现读写缓冲区接口的对象。调用者必须通过其他方法确定缓冲区的长度,或者使用“w#”。只接受单段缓冲对象;对所有其他类型都引发类型错误(TypeError)。
“w#” (read-write character buffer) [char *, int] 与“s#”类似,但接受任何实现读写缓冲区接口的对象。char *变量设置为指向缓冲区的第一个字节,int设置为缓冲区的长度。只接受单段缓冲对象;对所有其他类型都引发类型错误(TypeError)。
“(items)” (tuple) [matching-items] 对象必须是一个Python序列,其长度是项中的格式单元数。C参数必须对应于项中的单个格式单元。序列的格式单元可以嵌套。

3、Python调用C/C++通过boost实现

我们使用和上面同样的例子,实现test2.cpp如下:

#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
using namespace boost::python;
int Add(const int x, const int y)
{
    return x + y;
} 

int Del(const int x, const int y)
{
    return x - y;
}
BOOST_PYTHON_MODULE(test2)
{
    def("Add", Add);
    def("Del", Del);
}

其中BOOST_PYTHON_MODULE的参数为要导出的模块名字,编译命令如下:

g++ test2.cpp -fPIC -shared -o test2.so -I/usr/include/python2.7 -I/usr/local/include -L/usr/local/lib -lboost_python

注意: 编译时需要指定boost头文件和库的路径,我这里分别是/usr/local/include和/usr/local/lib

或者通过setup.py导出模块:

#!/usr/bin/env python
from distutils.core import setup
from distutils.extension import Extension
setup(name="PackageName",
 ext_modules=[
  Extension("test2", ["test2.cpp"],
  libraries = ["boost_python"])
 ])

Extension的第一个参数为模块名,第二个参数为文件名
执行如下命令:

python setup.py build

这时会生成build目录,找到里面的test2.so,并进入同一级目录,验证如下:

>>> import test2
>>> test2.Add(1,2)
3
>>> test2.Del(1,2)
-1

4、Python调用C/C++通过导出类

test3.cpp实现如下:

#include <boost/python.hpp>
using namespace boost::python;
class Test
{
    public:
        int Add(const int x, const int y)
        {
            return x + y;
        } 

        int Del(const int x, const int y)
        {
            return x - y;
        }
};
BOOST_PYTHON_MODULE(test3)
{
    class_<Test>("Test")
        .def("Add", &Test::Add)
        .def("Del", &Test::Del);
}

注意:BOOST_PYTHON_MODULE里的.def使用方法有点类似Python的语法,等同于:

class_<Test>("Test").def("Add", &Test::Add);
class_<Test>("Test").def("Del", &Test::Del);

编译命令如下:

g++ test3.cpp -fPIC -shared -o test3.so -I/usr/include/python2.7 -I/usr/local/include/boost -L/usr/local/lib -lboost_python

测试如下:

>>> import test3
>>> test = test3.Test()
>>> test.Add(1,2)
3
>>> test.Del(1,2)
-1

5、Python调用C/C++通过导出变参函数

test4.cpp实现如下:

#include <boost/python.hpp>
using namespace boost::python; 

class Test
{
    public:
        int Add(const int x, const int y, const int z = 100)
        {
        return x + y + z;
        }
}; 

int Del(const int x, const int y, const int z = 100)
{
    return x - y - z;
} 

BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(Add_member_overloads, Add, 2, 3)
BOOST_PYTHON_FUNCTION_OVERLOADS(Del_overloads, Del, 2, 3) 

BOOST_PYTHON_MODULE(test4)
{
    class_<Test>("Test")
        .def("Add", &Test::Add, Add_member_overloads(args("x", "y", "z"), "something"));
    def("Del", Del, Del_overloads(args("x", "y", "z"), "something"));
    }

这里Add和Del函数均采用了默认参数,Del为普通函数,Add为类成员函数,这里分别调用了不同的宏,宏的最后两个参数分别代表函数的最少参数个数和最多参数个数
编译命令如下:

g++ test4.cpp -fPIC -shared -o test4.so -I/usr/include/python2.7 -I/usr/local/include/boost -L/usr/local/lib -lboost_python

测试如下:

>>> import test4
>>> test = test4.Test()
>>> print test.Add(1,2)
103
>>> print test.Add(1,2,z=3)
6
>>> print test4.Del(1,2)
-1
>>> print test4.Del(1,2,z=3)
-1

6、Python调用C/C++通过导出带Python对象的接口

既然是导出为Python接口,调用者难免会使用Python特有的数据结构,比如tuple,list,dict,由于原生态方法太麻烦,这里只记录boost的使用方法,假设要实现如下的Python函数功能:

def Square(list_a)
{
    return [x * x for x in list_a]
}
``
`
即对传入的list每个元素计算平方,返回list类型的结果,代码如下:

```cpp
#include <boost/python.hpp> 

boost::python::list Square(boost::python::list& data)
{
    boost::python::list ret;
    for (int i = 0; i < len(data); ++i)
    {
        ret.append(data[i] * data[i]);
    } 

    return ret;
}
BOOST_PYTHON_MODULE(test5)
{
    def("Square", Square);
}

编译命令如下

g++ test5.cpp -fPIC -shared -o test5.so -I/usr/include/python2.7 -I/usr/local/include/boost -L/usr/local/lib -lboost_python

测试如下:

>>> import test5
>>> test5.Square([1,2,3])
[1, 4, 9]

boost实现了boost::python::tuple, boost::python::list, boost::python::dict这几个数据类型,使用方法基本和Python保持一致,具体方法可以查看boost头文件里的boost/python/tuple.hpp及其它对应文件
另外比较常用的一个函数是boost::python::make_tuple() ,使用方法如下

boost::python::tuple(int a, int b, int c)
{
    return boost::python::make_tuple(a, b, c);
}

以上就是python模块与C和C++动态库相互调用实现过程示例的详细内容,更多关于python模块与C和C++动态库相互调用的资料请关注我们其它相关文章!

(0)

相关推荐

  • Python调用C/C++动态链接库的方法详解

    本文以实例讲解了Python调用C/C++ DLL动态链接库的方法,具体示例如下: 示例一: 首先,在创建一个DLL工程(本例创建环境为VS 2005),头文件: //hello.h #ifdef EXPORT_HELLO_DLL #define HELLO_API __declspec(dllexport) #else #define HELLO_API __declspec(dllimport) #endif extern "C" { HELLO_API int IntAdd(in

  • 详解python如何调用C/C++底层库与互相传值

    前言 开发环境: Centos 7 + Python 3.5.1 + Qt Creator(只是使用Qt Creator编译而已,并没有使用QT的任何库) Python调用C/C++库,我现在能做到的有两种方式 1.extern "C" 导出(互相传值比较麻烦,不建议使用这种方式): 将C/C++库做成和平常一样的DLL和或者.so,比如: //.h文件 #include <Python.h> //.cpp文件 //C/C++ my.so 或者my.dll enter &q

  • 胶水语言Python与C/C++的相互调用的实现

    准备工作: python:https://www.python.org/downloads/ Dev-C++:https://sourceforge.net/projects/orwelldevcpp/ gcc和g++:http://mingw-w64.org/doku.php notepad++:https://notepad-plus.en.softonic.com/ 一.Python调用C 步骤1:Csayhello.c #include<stdio.h> void show_hello

  • Python与C/C++的相互调用案例

    一.问题 Python模块和C/C++的动态库间相互调用在实际的应用中会有所涉及,在此作一总结. 二.Python调用C/C++ 1.Python调用C动态链接库 Python调用C库比较简单,不经过任何封装打包成so,再使用python的ctypes调用即可. (1)C语言文件:pycall.c /***gcc -o libpycall.so -shared -fPIC pycall.c*/ #include <stdio.h> #include <stdlib.h> int f

  • Python调用C++程序的方法详解

    前言 大家都知道Python的优点是开发效率高,使用方便,C++则是运行效率高,这两者可以相辅相成,不管是在Python项目中嵌入C++代码,或是在C++项目中用Python实现外围功能,都可能遇到Python调用C++模块的需求,下面列举出集中c++代码导出成Python接口的几种基本方法,一起来学习学习吧. 原生态导出 Python解释器就是用C实现,因此只要我们的C++的数据结构能让Python认识,理论上就是可以被直接调用的.我们实现test1.cpp如下 #include <Pytho

  • python模块与C和C++动态库相互调用实现过程示例

    目录 Python调用C/C++ 1.Python调用C动态链接库 C语言文件:pycall.c gcc编译生成动态库libpycall.so Python调用动态库的文件:pycall.py 运行结果: 2.Python调用C/C++原生态导出 3.Python调用C/C++通过boost实现 4.Python调用C/C++通过导出类 5.Python调用C/C++通过导出变参函数 6.Python调用C/C++通过导出带Python对象的接口 Python调用C/C++ 1.Python调用

  • python爬虫lxml库解析xpath网页过程示例

    目录 前言 (一)xpath是什么 (二)xpath的基本语法 路径查询. (三) lxml库 (四)lxml库的使用 导入lxml.etree (五)实例演示 前言 在我们抓取网页内容的时候,通常是抓取一整个页面的内容,而我们仅仅只是需要该网页中的部分内容,那该如何去提取呢?本章就带你学习xpath插件的使用.去对网页的内容进行提取. (一)xpath是什么 xpath是一门在XML文档中查找信息的语言,xpath可用来在XML 文档中对元素和属性进行遍历,主流的浏览器都支持xpath,因为h

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

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

  • 分析Windows和Linux动态库

    摘要:动态链接库技术实现和设计程序常用的技术,在Windows和Linux系统中都有动态库的概念,采用动态库可以有效的减少程序大小,节省空间,提高效率,增加程序的可扩展性,便于模块化管理.但不同操作系统的动态库由于格式 不同,在需要不同操作系统调用时需要进行动态库程序移植.本文分析和比较了两种操作系统动态库技术,并给出了将Visual C++编制的动态库移植到Linux上的方法和经验. 1.引言 动态库(Dynamic Link Library abbr,DLL)技术是程序设计中经常采用的技术.

  • 详解dll动态库的开发与调用及文件的读写小程序

    详解dll动态库的开发与调用及文件的读写小程序 首先我们先来学习一下动态库的调用,先找到动态库的.dll和.lib文件并将其导入到同源文件相同级别的文件夹下面,然后在添加进其头文件,并右击项目处,然后点击链接,链接我们的lib文件(一定要是全名称包括扩展名),然后我们就可以调用动态库的函数了. Dll是我们具体的函数, lib使我们的函数描述文件. #include <stdio.h> #include <stdlib.h> /* 该代码是对文件读写操作的使用 */ #pragma

  • Qt动态库调用宿主进程中的对象方法纯虚函数使用

    目录 引言 在运行时加载动态库并获取对象指针(QLibrary) 本贴重点:在动态库中调用宿主进程的对象方法 还是以add方法为例写一个Demo 引言 可执行程序加载动态库并调用动态库导出的函数是比较容易的: 导入库对应的头文件 在CPP文件中调用函数 在链接程序时加上动态库作为参数 假设demo.cpp中需要用到动态库libadd.so中的某个函数,可能是int add(int x, int y),那么我们编译时就需要链接上libadd.so, gcc参数中-L./libs指定了当前目录下的l

  • 如何使用python的ctypes调用医保中心的dll动态库下载医保中心的账单

    需求:根据医保中心的文档和提供的dll动态库调用相关接口下载医保中心的账单. 文档:对调用dll动态库的描述,调用哪个dll文件,同时了解清楚调用这个dll文件中的哪个函数. 分析:结合文档及相关介绍弄清楚相关接口调用流程,从以上可以看出接口调用的是SiInterface.dll文件,然后先调用INIT函数进行初始化,然后再调用BUSINESS_HANDLE函数在医保局签到,然后在次调用BUSINESS_HANDLE函数下载账单,同时根据文档分析出每次调用函数的出入参.(具体的调用流程及每个函数

  • 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

  • Python模块汇总(常用第三方库)

    模块 定义 计算机在开发过程中,代码越写越多,也就越难以维护,所以为了编写可维护的代码,我们会把函数进行分组,放在不同的文件里.在python里,一个.py文件就是一个模块 优点: 提高代码的可维护性. 提高代码的复用,当模块完成时就可以在其他代码中调用 引用其他模块,包含python内置模块和其他第三方模块 避免函数名和变量名等名称冲突 Python语言生态 Python语言提供超过15万个第三方库,Python库之间广泛联系.逐层封装. 使用pip安装 Python社区:https://py

随机推荐