Qt(C++)与python混编经验记录

简述

刚好遇到了需求, 研究了下python和C/C++的混合编程. 对比了原生API和Cython, SWIG这类扩展库, 最终还是决定采用原生API方案.

  • 混合编程问题分为在C++中插入python 和 在python中插入C++, 本文只讨论在python中插入C++代码的情况.

Python3/C API 参考手册: https://docs.python.org/3/c-api/index.html
Python2/C API 参考手册: https://docs.python.org/2.7/c-api/index.html

环境

Win10 64bit
Qt 5.10.0 (MSVC 2015, 32 bit) with MinGW 32bit
Python 3.6.3 32bit

实现

  1. 代码
#include <Python.h>
int Widget::pow_function_from_python(int a, int b) {
    Py_Initialize();//初始化python解释器
    if ( !Py_IsInitialized() ) return -1;//检查初始化

    int res;
    PyObject *pModule,*pFunc;
    PyObject *pArgs, *pValue;

    pModule = PyImport_Import(PyUnicode_FromString("math"));//导入模块
    pFunc = PyObject_GetAttrString(pModule, "pow");//获取函数
    if ( !pFunc || !PyCallable_Check(pFunc) ) return -1;//检查函数是否存在

    //构造参数
    pArgs = PyTuple_New(2);
    PyTuple_SetItem(pArgs,0, Py_BuildValue("i",a));
    PyTuple_SetItem(pArgs,1, Py_BuildValue("i",b));

    //调用函数
    pValue = PyObject_CallObject(pFunc, pArgs);
    //处理返回值
    res = PyLong_AsLong(pValue);

    Py_Finalize();
    return res;
}
  • 所有Python元素,module、function、tuple、string等等,实际上都是PyObject。C语言里操纵它们,一律使用PyObject *。
  • Python的类型与C语言类型可以相互转换。Python类型XXX转换为C语言类型YYY要使用PyXXX_AsYYY函数;
  • C类型YYY转换为Python类型XXX要使用PyXXX_FromYYY函数。
  • 也可以创建Python类型的变量,使用PyXXX_New可以创建类型为XXX的变量。
  • 若a是Tuple,则a[i] = b对应于 PyTuple_SetItem(a,i,b),有理由相信还有一个函数PyTuple_GetItem完成取得某一项的值。
  • 不仅Python语言很优雅,Python的库函数API也非常优雅。
  1. 头文件
#include <Python.h>

注意: python.h 中定义了一些预处理宏, 可能与其他标准头文件冲突, 故必须将python.h 在其他头文件之前包含. 在Qt中, python的object.h使用slot作为变量名, 与Qt的宏定义冲突, 可如下修改避免.

#undef slots            //这里取消slots宏定义
PyType_Slot *slots; /* terminated by slot==0. */
#define slots Q_SLOTS   //这里再恢复
  1. 函数介绍

void Py_Initialize()
初始化python解释器. 此函数应该在所有python API函数使用前被调用(Py_SetProgramName(), Py_SetPythonHome() 及 Py_SetPath()例外), 重复调用时为空操作.

Py_IsInitialized()
返回python解释器初始化状态. 强烈建议检查初始化状态, 未初始化情况下直接调用API函数将引起程序崩溃, 且无任何调试信息.

int PyRun_SimpleString()
执行一行简单的python代码, 成功返回0, 失败返回-1, 执行失败无调试信息.

Py_Finalize()
取消所有python解释器的初始化, 回收资源.

PyImport_Import()
导入模块, 对应python代码中的import.

PyUnicode_FromString()
将C字符串转化为python Unicode字符串.(仅python3支持).

PyObject_GetAttrString()
获取python函数.

PyTuple_New
构造python元组.

PyTuple_SetItem()
设置元组变量.

PyObject_CallObject()
调用python函数.

PyLong_AsLong
转换python中的long类型数据为C语言long型.

Py_BuildValue()
把C++的变量转换成一个Python对象。当需要从C++传递变量到Python时,就会使用这个函数。此函数有点类似C的printf,但格式不同。常用的格式有
s 表示字符串
i 表示整型变量
f 表示浮点数
O 表示一个Python对象.

  1. 编译

需添加python的头文件和库文件 在Qt中具体做法为在pro文件中添加:

INCLUDEPATH += -I C:\Users\AndyF\AppData\Local\Programs\Python\Python36-32\include
LIBS += -LC:\Users\AndyF\AppData\Local\Programs\Python\Python36-32\libs -lpython36

在VisualStudio下, 可用命令进行编译

cl my_python.c -IC:\Python27\include C:\Python27\libs\python27.lib

在linux下, 编译命令为

gcc my_python.c -o my_python -I/usr/include/python2.7/ -lpython2.7

参考文献

c++与Python3互相调用
QT与Python混合编程经验记录
在c中内嵌python编程
如何实现 C/C++ 与 Python 的通信?
C++嵌入Python程序(二):参数传入及返回
C++调用Python浅析

Comments
Write a Comment
  • Daby reply

    沙发沙发!