C++调用Python,嵌套列表输入转化、输出解析,以及简单调试


由于在Python和C++方面都是新手,然后又要在ROS系统中做仿真,折腾了两天才把这个折腾好。。真是苦不堪言,关于基本的调用函数网上已经有很多博文有说明,本文主要解决的是复杂列表输入,复杂列表输出下的问题,还有一些简单的避坑和调试技巧。

环境

关于环境配置,是在ROS系统中使用C++调用python,其中ROS是kinetic版本,其内置的python版本为2.7。关于ROS工程中的设置参看该博文,此处主要说调用过程中出现的问题。

代码及说明

首先要在头文件中导入Python与C++的接口文件,其中内置了各种API函数。具体的API函数可以在官网查询。

#include <python/Python.h> 

之后在主程序中即可调用API函数,下一步即可导入自定义的模块,并导入模块中的函数。

// load python
Py_Initialize();
PyRun_SimpleString("import sys");   
PyRun_SimpleString("sys.path.append('/home/ych')"); // add the path of Python file
PyObject *pModule = PyImport_ImportModule("debug_py");//debug_py: Python filename
PyObject *pFunc = PyObject_GetAttrString(pModule, "print_input");//print_input: Function name

debug_py是我写的一个调试用的py文件,即将输入打印出来并返回输入值:

def print_input(a):
    print(a)
    return a

现在即可进行函数的输入转化及输出解析,此部分借鉴了一篇知乎的文章一篇腾讯云的文章
输入输出的核心函数分别是Py_BuildValue以及PyArg_ParseTuple。前者是用来将C++的变量类型转化为python中的变量类型,后者则反之。注意Py_BuildValue可将C++变量转化为指定的python变量,返回PyObject类型。而PyArg_ParseTuple在讲python对象写出时直接写入某变量的地址。且官网有提醒:The characters space, tab, colon and comma are ignored in format strings (but not within format units such as s#)。

int leng_list = 20;
PyObject *pList = PyList_New(leng_list);
int k = 0;
while(k<leng_list){
    
    
    PyList_SET_ITEM(pList, k, Py_BuildValue("[d, d]", (double)k, (double)k));
    k++;
}
PyObject *pInput = PyTuple_New(1);
PyTuple_SetItem(pInput, 0, pList);
PyObject *pyReturn = PyEval_CallObject(pFunc, pInput);

double return_list[leng_list][2];
PyObject *list_item = NULL;
PyObject *tuple_item = NULL;
for(int j = 0; j<leng_list; j++){
    
    
    list_item = PyList_GetItem(pyReturn, j);
    tuple_item =  PyList_AsTuple(list_item);
    PyArg_ParseTuple(tuple_item, "dd", &return_list[j][0], &return_list[j][1]);
}
Py_Finalize();

避坑及调试

根据我的调试经验,调试过程是渐进式的,在PyImport_ImportModule、PyEval_CallObject、PyArg_ParseTuple也即函数的导入、输入转化、输出解析三个关键节点都有可能出问题,Python接口函数只要出错了就返回NULL,然后就会报段错误(segmentation fault),并不会出现详细的报错提示,踩了好多坑才过来。因此得循序渐进的调试,前一个跑通了再试下一个。在调试过程中的一些体会如下:

  1. 由于python对于格式的敏感性,在调试过程中,如果更改了python文件,最好要在python的IDE中重新编译一下,确认无误再导入。
  2. 其实C++中调用python与直接在python中编写是极其相似的,只不过在所有原本的python类型外面又包装了一层PyObject类型。而且还可以看到某PyObject类型下是什么python类型。注意查看PyObject的python类型,在接口函数的输入转化及输出解析阶段都有帮助。可以在调试器的watch窗口中使用*name来查看。
  3. 注意利用PyList_Size等函数来查看变量的长度等属性。
  4. 由于C++调用python时难以在python中打断点,貌似也有一些大神配置好了可以打断点的调试器,但是并没有看到很详细的教程。因此可以利用python中print来简单调试,以查看python函数运行的一些信息。以与python编译器中的调试信息相比较。
  5. 在调试过程中,为了看python函数的输出,有时候会将Py_Finalize()写在靠前的位置。但需要注意的是:有些API接口函数是可以在Py_Finalize()之后运行的,比如PyList_GetItem,但是有些在Py_Finalize()之后运行就可能会出错,比如PyArg_ParseTuple。

猜你喜欢

转载自blog.csdn.net/qq_15950515/article/details/85762493
今日推荐