2018-11-25随笔-今天谈谈C++嵌入Python脚本中遇到的问题

由于现在很多底层协议用C/C++,然后机器学习或者深度学习等算法模型使用基于Python的TensorFlow来实现。所以现在C++用来做框架,做软件界面,然后调用Python的算法脚本来进行计算是很常见的需求。

我们的项目中也存在着这样的需求。下面来记录一下相应的实现方式。

背景:C++上用MFC做界面,Python上是import了numpy与pandas模块的处理功能(后续的基于sklearn与TensorFlow的脚本还没嵌入,配置方式相同,后续实验会再放上来)

软件配置:win10/64位+VS2013+Python3.6/64位

  • 在Python的安装目录下,找到libs文件夹,将其中的python36.lib复制一份并命名python36_d.lib,功能是用于VS的调试
  • 在VS中新建项目(随便什么项目,我使用的是控制台程序),在项目-项目属性中设置活动平台为X64,目的是匹配Python的版本
  • 在VS的项目属性里面:C++->常规->附加包含目录,输入..\include
  • 链接器->常规->附加目录项,输入..\libs;链接器->输入->附加依赖项,添加python36.lib;python36_d.lib;
  • python36.dll拷贝到Debug目录下(注意是项目名->X64->debug文件夹里面,与***.exe同目录);将py文件拷贝到Debug目录下(同上)

我的py文件中,需要调用的是其中一个readcase函数,其中需要传入一个文件路径参数,再对文件里面的内容进行相应的处理。

C++代码(这里面我参考了网上的一些案例,上上了一些注释-_-||,需要注意的点我在后面再补充)

 1 #include <iostream>
 2 #include <Python.h>
 3 using namespace std;
 4 
 5 extern "C"
 6 {
 7 #include "Python.h"
 8 }
 9 //调用输出"Hello Python"函数
10 void Hello()  //没有传参的话就没啥问题
11 {
12     Py_SetPythonHome(L"G:\Python3.6.3_ 64");
13     Py_Initialize();//调用Py_Initialize()进行初始化
14     PyObject * pModule = NULL; //声明在C++中的用到的Python变量
15     PyObject * pFunc = NULL; 
16     pModule = PyImport_ImportModule("c_usepython");//调用的Python文件名,test_Ctopython
17     pFunc = PyObject_GetAttrString(pModule, "Hello");//调用的函数名
18     PyEval_CallObject(pFunc, NULL);//调用函数,NULL表示参数为空
19     Py_Finalize();//调用Py_Finalize,和Py_Initialize相对应的.
20 }
21 //调用Add函数,传两个int型参数
22 void Add()  //测试传参的例子,
23 {
24     Py_SetPythonHome(L"G:\Python3.6.3_ 64");
25     Py_Initialize();
26     PyObject * pModule = NULL;
27     PyObject * pFunc = NULL;
28     pModule = PyImport_ImportModule("c_usepython");//Python文件名
29     pFunc = PyObject_GetAttrString(pModule, "Add");//Add:Python文件中的函数名
30     //创建参数:
31     PyObject *pArgs = PyTuple_New(2);//函数调用的参数传递均是以元组的形式打包的,2表示参数个数
32     PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", 6));//0--序号,i表示创建int型变量
33     PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 8));//1--序号
34     //返回值
35     PyObject *pReturn = NULL;
36     pReturn = PyEval_CallObject(pFunc, pArgs);//调用函数
37     //将返回值转换为int类型
38     int result;
39     PyArg_Parse(pReturn, "i", &result);//i表示转换成int型变量
40     cout << "6 + 8 = " << result << endl;
41     //Py_Finalize();
42 }
43 void Readcase2()  //
44 {     /
45     char* picpath = "D:\\file111.dat";//需要读取的路径参数,这里的路径太长就一直无法正常运行,C++里面没有报错,但是调用老是失败,返回py里面的threading assert tlocked()错误,我觉得是因为参数没有传进去,Python才报错
46     cout<<picpath<<endl;
47     Py_SetPythonHome(L"G:\Python3.6.3_ 64");
48     Py_Initialize();
49     //PyEval_InitThreads(); //初始化进程
50     PyObject * pModule = NULL;
51     PyObject * pFunc = NULL;
52     PyObject* pRetVal = NULL;
53     //PyObject * pArgs=NULL ;
54     pModule = PyImport_ImportModule("c_usepython");//Python文件名
55     pFunc = PyObject_GetAttrString(pModule, "readcase");//Python文件中的函数名
56     //创建参数:
57     PyObject *pArgs = PyTuple_New(1);//函数调用的参数传递均是以元组的形式打包的,2表示参数个数
58     PyTuple_SetItem(pArgs, 0, Py_BuildValue("s", picpath));//0--序号,i表示创建int型变量
59     //pArgs = PyTuple_New(1);
60     //PyTuple_SetItem(pArgs,0,Py_BuildValue("s", "G:\\实验室工作\\CT项目软件工作\\75-1-dao"));//这个字符串太长了,导致调用失败
61     //PyObject *pReturn = NULL; //我这个函数不需要返回,具体工作都在Python里面做了,以后会返回数组来进行下一步工作
62     //PyGILState_STATE gstate;  //这个是关于GIL的一些内容,具体功能不太了解,当初调试的时候失败,似乎没啥用
63     //gstate = PyGILState_Ensure();
64     //PyEval_CallObject(pFunc, pArgs);
65     PyEval_CallObject(pFunc, pArgs);//调用函数,PyObject_CallObject()这个函数也可以,两者功能很相似
66     //PyGILState_Release(gstate);
67     //将返回值转换为int类型
68     //char result;
69     //PyArg_Parse(pReturn, "s", &result);//s表示转换成string型变量
70     //cout << "结果: " << result << endl;
71     Py_Finalize();
72 }
73 int main(int argc, char** argv)
74 {
75     //cout << "调用Test001.py中的Hello函数..." << endl;
76     ////Hello();
77     //cout << "\n调用Test001.py中的Add函数..." << endl;
78     //Add();
79     cout << "\n尝试调用test_Ctopython中的readcase文件..." << endl;
80     Readcase2();
81     system("pause");
82     return 0;
83 }

结果能跑起来,暂时先这样吧,想到什么再说吧。后面的TensorFlow实验,有时间做了再上传。

猜你喜欢

转载自www.cnblogs.com/ddl-2018/p/10017612.html