1. 程式人生 > >2018-11-25隨筆-今天談談C++嵌入Python指令碼中遇到的問題

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實驗,有時間做了再上傳。