2008-10-18 2 views
11

Khi làm việc trên một dự án C++, tôi đang tìm kiếm thư viện của bên thứ ba cho một thứ không phải là hoạt động kinh doanh cốt lõi của tôi. Tôi tìm thấy một thư viện thực sự tốt, làm chính xác những gì cần thiết, nhưng nó được viết bằng Python. Tôi đã quyết định thử nghiệm với việc nhúng mã Python vào C++, sử dụng thư viện Boost.Python.Python được nhúng trong CPP: cách lấy dữ liệu trở lại CPP

C++ mã trông một cái gì đó như thế này:

#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 (rất) phiên bản đơn giản hóa của mã Python trông như thế này:

import thirdparty 

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

Bây giờ vấn đề là thế này: 'MyFunc' thực hiện tốt, tôi có thể thấy bản in của 'kết quả'. Những gì tôi không thể làm là đọc 'kết quả' trở lại từ mã C++. Lệnh trích xuất không bao giờ tìm thấy 'kết quả' trong bất kỳ không gian tên nào. Tôi đã thử xác định 'kết quả' là toàn cầu, tôi thậm chí đã thử trả về một bộ tuple, nhưng tôi không thể làm cho nó hoạt động.

Trả lời

6

Trước hết, hãy thay đổi chức năng của bạn thành return giá trị. print ing nó sẽ làm phức tạp mọi thứ kể từ khi bạn muốn có được giá trị trở lại. Giả sử MyModule.py vẻ của bạn như thế này:

import thirdparty 

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

Bây giờ, để làm những gì bạn muốn, bạn phải vượt qua nhúng cơ bản, như documentation says. Dưới đây là toàn bộ mã để chạy chức năng của bạn:

#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

câu trả lời Phần lớn toàn diện hơn so với tôi, từ (tôi giả sử) cho cha mẹ đồng :) nosklo, tôi đề nghị bạn mở rộng câu trả lời của bạn với một ví dụ PyRun_String; nó cho phép linh hoạt hơn. – tzot

+0

Tôi nghĩ rằng 'pArgs = PyTuple_New (0);' sẽ vượt qua 1 không phải 0. –

0

Bạn sẽ có thể trả lại kết quả từ MyFunc, sau đó sẽ kết thúc trong biến mà bạn hiện đang gọi là "bị bỏ qua". Điều này giúp loại bỏ sự cần thiết phải truy cập nó theo bất kỳ cách nào khác.

+0

Không có vấn đề gì được trả về từ các MyFunc, tôi nhận được: Lỗi Loại: 'NoneType' đối tượng không hỗ trợ phân mục –

1

Tôi nghĩ những gì bạn cần là PyObject_CallObject(<py function>, <args>), trả về giá trị trả về của hàm bạn gọi là PyObject hoặc PyRun_String(<expression>, Py_eval_input, <globals>, <locals>) đánh giá một biểu thức và trả về kết quả của nó.

3

Dựa trên ΤΖΩΤΖΙΟΥ, Josh và câu trả lời Nosklo của tôi cuối cùng đã nhận nó làm việc sử dụng 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; 
} 

Một số điểm quan trọng:

  1. Tôi đã thay đổi 'exec' thành 'exec_file' ra khỏi số tiện lợi, nó cũng hoạt động với đồng bằng 'exec'.
  2. Lý do chính nó thất bại là i đã không vượt qua một "địa phương" name_sapce để 'exec' hoặc 'exec_file' - đây tại là cố định bằng cách thông qua name_space hai lần.
  3. Nếu chức năng trăn trở chuỗi unicode, họ không mui trần để 'std :: string', vì vậy tôi đã phải hậu tố tất cả các chuỗi python với '.encode ('ASCII', 'bỏ ​​qua')'.