【Python/C++混合编程——基于API接口】

本文详细介绍了如何在C++中通过API接口调用Python代码,包括CmakeList.txt的配置、工程文件结构、混合编程的实现步骤、字符串传递问题以及循环调用Python文件的注意事项。在实践中,作者发现混合编程虽有挑战,但能有效实现C++与Python的结合。
摘要由CSDN通过智能技术生成


前言

C++调用python不只有ROS一种方法,其实我更喜欢调用API接口的这种方法。当然,按我的理解这种方法本质上类似于开启了一个python的虚拟机运行环境。虽然我感觉效率比ROS高,但是并没有验证。一家之言,希望大家辩证看待。


一、准备工作

既然已经做到了混合编程了,那我就默认这项工作应该是一个工程。而且我的项目中,python涉及到了深度学习。那我就以此为例,从头到尾记录一下。

1.CmakeList.txt文件的书写

project(hello_cpython)
cmake_minimum_required(VERSION 3.10)
include_directories("include")
add_library(/*你src中的cpp文件添加到library*/)

add_executable(cnn /*你src中的cpp主程序,cnn什么的的名字自己起*/)
/*以下这句是与混合编程最有关的两句之一,1/2*/
include_directories(
    /home/xxxx/anaconda3/envs/slam/include/python3.6m
)
//路径注释(/home/xxxx/anaconda3/,xxxx是你电脑主文件夹的名字;/envs/slam/include/python3.6m,slam是我的虚拟环境的名字,最后的3.6,其实与你当时配置虚拟环境设定的python版本有关)
target_link_libraries(cnn /*添加入library的cpp文件*/)
/*以下这句是与混合编程最有关的两句之一,2/2*/
target_link_libraries(cnn
    /home/filetransfer/anaconda3/envs/slam/lib/libpython3.6m.so
)
//不再注释路径,与之前的相似,根据自己的实际情况修改
set(CMAKE_CXX_FLAGS "-std=c++11") 

2.工程文件的放置

工程文件夹名称:c_python
子文件夹:src include build
要调用的python文件放在build中

二、混合编程方法

1.代码举例说明

以下代码为cpp主程序的逻辑路径,并不能直接运行,请根据自己的实际情况略作修改。

/*1.混合编程所必需的头文件*/
#include <Python.h>

#include <iostream>
using namespace std;

/*2.主函数*/
int main(int argc, char *argv[]) {
    int CurrentFrameID,r1;
    Py_Initialize();
    /*2.1判断初始化是否成功*/
    if(!Py_IsInitialized())
    {
        printf("Python init failed!\n");
        return -1;
    }
    /*2.2将当前路径添加到虚拟运行环境*/
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('./')");
    PyRun_SimpleString("print(sys.path)");//会输出一大堆路径,只要其中包括./就OK
    /*2.3建立模型和函数指针*/
    PyObject* pModule = NULL;//模型指针代表.py函数
    PyObject* pyFunc_mix = NULL;//函数指针代表.py中的指定函数
	/*2.4导入python文件*/
    pModule = PyImport_ImportModule("demo");//根据自己的情况修改,我的python文件为demo.py
    if (!pModule) {
        printf("Can not open python file!\n");
        PyErr_Print();//打印出python运行失败的报错原因
    	PyErr_Clear();//这两句哪里需要哪里搬,否则不会输出python文件的报错原因
        return -1;
    }
    /*2.5调用python文件中的函数*/
    pyFunc_mix = PyObject_GetAttrString(pModule, "hello_cpython");
   	if(pModule && PyCallable_Check(pyFunc_mix))
	{
		/*2.6添加c++输入python函数*/
		PyObject *pyParams = PyTuple_New(2);//2代表一个元组包括两个变量
		PyTuple_SetItem(pyParams,0,Py_BuildValue("i",CurrentFrameID));//第一个变量,整形为i,字符串为s,double是d
		PyTuple_SetItem(pyParams,1,Py_BuildValue("i",CurrentFrameID+1));//第二个变量
		/*2.7运行python文件*/
		PyObject *pyValue = PyObject_CallObject(pyFunc_mix,pyParams);//PyObject_CallObject第一个变量是函数,第二个是参数元组
		Py_XDECREF(pyParams);
		PyArg_ParseTuple(pyValue,"i",&r1);//读取python输出变量(即hello_cpython函数return的结果)
		/*2.8输出读取结果*/
		if(pyValue)
		{
			cout<<r1<<endl;
		}
	} 
    /*2.5退出虚拟环境*/ 
    Py_XDECREF(pyValue);
	Py_XDECREF(pyFunc_mix); 
    Py_DECREF(pModule);
    Py_Finalize();
    return 0;
}

至于python文件完全不需要修改,只需要保证python与c++传参类型和数量一致就好了。

cmake ..
make

得到了一个名为cnn的可执行文件

./cnn

2.传递string的问题

当我传递int和double类型的数据时,都没有出现问题,但是一旦以这种方式传递string类型的数据,就无法传递成功

string C_ID="hello";
PyTuple_SetItem(pyParams,0,Py_BuildValue("s",C_ID));

但是以这种形式就可以顺利传输。

PyTuple_SetItem(pyParams,0,Py_BuildValue("s","hello"));

但是这样就丧失了灵活性,反正很神奇,如果有同学知道如何解决请不吝赐教。

3.循环调用Python文件

如果在实际应用中,需要多次循环调用某一python文件,应该将python虚拟环境的初始化和终结放置在大循环之外。否则将有可能出现段错误。即,如下。

Py_Initialize();
for{
	/*这里循环调用python文件*/
}
Py_Finalize();

总结

以上,混合编程很坑,资料很少,祝好运!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值