简介:Python 中调用 C/C++ 程序的方法有多种,这里简单介绍使用 C/C++ 编写 Python扩展供 Python 中使用的方法。相较于使用 ctypes 加载 C/C++ 程序编译的动态库进而调用函数,扩展模块的方式,从Python 中传入参数以及从 C/C++ 程序获取返回值的过程更为规范,或者说,更能够减少程序出现错误。使用 ctypes 调用程序,在多线程 Python 程序中容易产生段错误(segfault)。
文章目录
1. 扩展模块的作用
C 扩展可以用来做两件不能直接在 Python 中做的事情: 构建新的内置对象类型、调用 C 库函数和系统调用。
为了支持扩展, Python 的 API 定义了许多函数、宏和变量,可以访问 Python 运行时系统的大部分内容。Python 的相关 API 可以通过在 C 文件中引入 “Python.h” 文件使用。
C 扩展接口依赖于 CPython ,扩展模块无法在其他 Python 实现上工作,因此可移植性较差。
2. 用 C++ 实现 Python 扩展
2.1 引入 Python API
如前所述,在 C++ 程序中使用 Python 的 API 需要在 C++ 程序中引入头文件 “Python.h”。而且由于 Python 可能会定义一些能在某些系统上影响标准头文件的预处理器定义,因此在包含任何标准头文件之前,必须先包含 Python.h。推荐总是在 Python.h 前定义 PY_SSIZE_T_CLEAN,实践证明,构建扩展后续是依赖这个宏定义的。因此 C++ 程序的文件头部应当如下所示:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
2.2 添加 C++ 函数到扩展模块
自己定义的模块名称为 demo ,文件名称按照习惯可以命名为 demomodule.cpp,也可以不这么做,模块名的决定性因素为下边 PyModuleDef 类型的结构体。在模块 demo 下可以调用的函数,其函数名格式为 demo_funcname,如下 demo_myFunc 为 demo 模块下 myFunc 函数的定义。一个模块可以定义多个函数。
函数的参数列表形式固定,均为(PyObject *self, PyObject *args):
这里self与常用python类中定义函数的 self 类似,无需多看,这里的 args 则是函数接收的参数列表。
具体接收参数的内容,需要在函数定义内部 PyArg_ParseTuple 函数中确定。此函数负责解析在 Python 中调用模块中函数时接收的参数,将其转换为对应的 C 类型数据。
函数返回值:
定义的函数可以返回对象,也可以直接返回数据。
如果返回的是数据,则需要使用 Py_BuildValue 函数,将 C 类型的数据转换为 Python 中的数据类型。此函数的作用与上述函数 PyArg_ParseTuple 的作用相反。
如果返回的是对象,则意味这需要在此函数中构建一个对象,之后将对象返回给 Python,这时候需要注意给对象增加引用计数,否则会导致内存泄漏的问题。
参见这里的7-10
添加 C++ 函数到 扩展模块的源码如下:
static PyObject *demo_myFunc(PyObject *self, PyObject *args)
{
/*
* 这里是一些数据准备工作,用于接收从 Python 传递来的参数,给需要调用的 C++ 函数使用,这里的参数
* 接收需要一些参数解析
*/
unsigned char *cipher_text;
int cipher_text_length;
unsigned char *iv_salt_file_path;
unsigned char result[DEFAULT_RSA_KEY_LEN] = {
0};
int len;
/*
* 对接收的参数进行解析,格式转换参见: https://docs.python.org/zh-cn/3/c-api/arg.html
* 这里的 s* 为接收 Python 中字符串或者 byte 类型的参数,将其解析为缓冲区中的内容, 这里的 i
* 为将 Python 中的整型解析为 C++ 中的整型
*/
if (!PyArg_ParseTuple(args, "s*is*", &cipher_text

843

被折叠的 条评论
为什么被折叠?



