2008-10-18 59 views
11

在處理C++項目時,我一直在尋找第三方庫來尋找不是我的核心業務的東西。我找到了一個非常好的庫,完成所需的任務,但它是用Python編寫的。我決定嘗試用C++嵌入Python代碼,使用Boost.Python庫。嵌入在CPP中的Python:如何將數據返回給CPP

的C++代碼看起來是這樣的:

#include <string> 
#include <iostream> 
#include <boost/python.hpp> 

using namespace boost::python; 

int main(int, char **) 
{ 
    Py_Initialize(); 

    try 
    { 
     object module((handle<>(borrowed(PyImport_AddModule("__main__"))))); 

     object name_space = module.attr("__dict__"); 
     object ignored = exec("from myModule import MyFunc\n" 
          "MyFunc(\"some_arg\")\n", 
          name_space); 

     std::string res = extract<std::string>(name_space["result"]); 
    } 
    catch (error_already_set) 
    { 
     PyErr_Print(); 
    } 

    Py_Finalize(); 
    return 0; 
} 

A(非常)簡化了Python代碼的版本是這樣的:

import thirdparty 

def MyFunc(some_arg): 
    result = thirdparty.go() 
    print result 

現在的問題是這樣的: 'MYFUNC'執行得很好,我可以看到'結果'的打印。 我不能做的是從C++代碼中讀取'結果'。提取命令從不會在任何名稱空間中找到「結果」。 我試着定義'結果'作爲一個全球性的,我甚至嘗試返回一個元組,但我無法得到它的工作。

回答

6

首先,將您的功能更改爲return的值。因爲你想要得到回報,所以它會使事情變得複雜。假設你MyModule.py看起來像這樣:

import thirdparty 

def MyFunc(some_arg): 
    result = thirdparty.go() 
    return result 

現在,做你想做什麼,你必須超越基本的嵌入,爲documentation says。以下是完整的代碼運行的功能:

#include <Python.h> 

int 
main(int argc, char *argv[]) 
{ 
    PyObject *pName, *pModule, *pFunc; 
    PyObject *pArgs, *pArg, *pResult; 
    int i; 

    Py_Initialize(); 
    pName = PyString_FromString("MyModule.py"); 
    /* Error checking of pName left out as exercise */ 

    pModule = PyImport_Import(pName); 
    Py_DECREF(pName); 

    if (pModule != NULL) { 
     pFunc = PyObject_GetAttrString(pModule, "MyFunc"); 
     /* pFunc is a new reference */ 

     if (pFunc) { 
      pArgs = PyTuple_New(0); 
      pArg = PyString_FromString("some parameter") 
      /* pArg reference stolen here: */ 
      PyTuple_SetItem(pArgs, 0, pArg); 
      pResult = PyObject_CallObject(pFunc, pArgs); 
      Py_DECREF(pArgs); 
      if (pResult != NULL) { 
       printf("Result of call: %s\n", PyString_AsString(pResult)); 
       Py_DECREF(pResult); 
      } 
      else { 
       Py_DECREF(pFunc); 
       Py_DECREF(pModule); 
       PyErr_Print(); 
       fprintf(stderr,"Call failed\n"); 
       return 1; 
      } 
     } 
     else { 
      if (PyErr_Occurred()) 
       PyErr_Print(); 
      fprintf(stderr, "Cannot find function"); 
     } 
     Py_XDECREF(pFunc); 
     Py_DECREF(pModule); 
    } 
    else { 
     PyErr_Print(); 
     fprintf(stderr, "Failed to load module"); 
     return 1; 
    } 
    Py_Finalize(); 
    return 0; 
} 
+1

比我更全面的答案,從(我想)研究員父:) nosklo,我建議您用PyRun_String示例擴展您的答案;它允許更多的靈活性。 – tzot 2008-10-19 08:58:02

0

您應該能夠從MyFunc返回結果,然後結果在您當前調用的「忽略」變量中。這消除了以任何其他方式訪問它的需要。

+0

不管從MYFUNC回來後,我得到: 類型錯誤:「NoneType」對象不支持項目分配 – 2008-10-19 00:39:52

1

我認爲您需要的是PyObject_CallObject(<py function>, <args>),它返回您稱爲PyObject的函數的返回值,或者PyRun_String(<expression>, Py_eval_input, <globals>, <locals>),它計算單個表達式並返回其結果。

3

基於ΤΖΩΤΖΙΟΥ,Josh和Nosklo的答案,我終於得到它的工作使用Boost.Python的:

的Python:

import thirdparty 

def MyFunc(some_arg): 
    result = thirdparty.go() 
    return result 

C++:

#include <string> 
#include <iostream> 
#include <boost/python.hpp> 

using namespace boost::python; 

int main(int, char **) 
{ 
    Py_Initialize(); 

    try 
    { 
     object module = import("__main__"); 
     object name_space = module.attr("__dict__"); 
     exec_file("MyModule.py", name_space, name_space); 

     object MyFunc = name_space["MyFunc"]; 
     object result = MyFunc("some_args"); 

     // result is a dictionary 
     std::string val = extract<std::string>(result["val"]); 
    } 
    catch (error_already_set) 
    { 
     PyErr_Print(); 
    } 

    Py_Finalize(); 
    return 0; 
} 

的一些要點:

  1. 我將'exec'更改爲'exec_file'中的 ,方便起見,它也適用於 明確的'exec'。
  2. 失敗的主要原因是我 沒有通過「本地」 name_sapce到 「EXEC」或「exec_file」 - 這是現在 通過傳遞name_space兩次固定。
  3. 如果蟒蛇函數返回 unicode字符串,它們不是 轉換爲 '的std :: string',所以我 不得不後綴的所有Python字符串 與 '.encode( 'ASCII', '忽略')'。