2010-07-20 39 views
34

Tôi muốn gọi hàm tùy chỉnh được định nghĩa trong mô-đun python từ C. Tôi có một số mã sơ bộ để làm điều đó, nhưng nó chỉ in đầu ra để stdout.Gọi phương thức python từ C/C++, và giải nén giá trị trả về

mytest.py

import math 

def myabs(x): 
    return math.fabs(x) 

test.cpp

#include <Python.h> 

int main() { 
    Py_Initialize(); 
    PyRun_SimpleString("import sys; sys.path.append('.')"); 
    PyRun_SimpleString("import mytest;"); 
    PyRun_SimpleString("print mytest.myabs(2.0)"); 
    Py_Finalize(); 

    return 0; 
} 

Làm thế nào tôi có thể trích xuất các giá trị trả về vào một đôi C và sử dụng nó trong C?

+2

Bạn đã đọc: http: // docs.python.org/c-api/? Có vẻ như để trả lời câu hỏi của bạn. http://docs.python.org/c-api/number.html#PyNumber_Float dường như là những gì bạn đang tìm kiếm. Có gì sai với nó? Bạn cần thêm gì nữa à? –

+1

Câu hỏi thực sự là cách truy cập đối tượng được trả về từ 'mytest.myabs (2.0)'. Một khi tôi có một con trỏ đến nó, tôi có thể dễ dàng chuyển đổi nó thành một phao bằng cách sử dụng hàm PyNumber_Float. – dzhelil

+1

Chúng ta có thể thấy câu trả lời với một ví dụ mã và được thực hiện với nó không? –

Trả lời

51

Như đã giải thích trước đây, sử dụng PyRun_SimpleString có vẻ là một ý tưởng tồi.

Bạn chắc chắn nên sử dụng các phương pháp được cung cấp bởi C-API (http://docs.python.org/c-api/).

Đọc phần giới thiệu là điều đầu tiên cần làm để hiểu cách hoạt động của nó.

Trước tiên, bạn phải tìm hiểu về PyObject là đối tượng cơ bản cho API C. Nó có thể đại diện cho bất kỳ loại kiểu cơ bản python nào (chuỗi, float, int, ...).

Nhiều chức năng tồn tại để chuyển đổi ví dụ chuỗi python thành char * hoặc PyFloat thành gấp đôi.

Thứ nhất, nhập khẩu mô-đun của bạn:

PyObject* myModuleString = PyString_FromString((char*)"mytest"); 
PyObject* myModule = PyImport_Import(myModuleString); 

Sau đó nhận được một tham chiếu đến chức năng của bạn:

PyObject* myFunction = PyObject_GetAttrString(myModule,(char*)"myabs"); 
PyObject* args = PyTuple_Pack(1,PyFloat_FromDouble(2.0)); 

Sau đó, nhận được kết quả của bạn:

PyObject* myResult = PyObject_CallObject(myFunction, args) 

Và nhận được trở lại với một đôi :

double result = PyFloat_AsDouble(myResult); 

Bạn rõ ràng nên kiểm tra lỗi (cf. liên kết được đưa ra bởi Mark Tolonen).

Nếu bạn có bất kỳ câu hỏi nào, đừng ngần ngại. Chúc may mắn.

+4

Tôi sẽ chấp nhận câu trả lời của bạn vì những lời khuyên hữu ích, nhưng nó sẽ có ích hơn cho tôi nếu bạn có thể cung cấp một mô-đun C làm việc hoàn chỉnh gọi hàm python ở trên. – dzhelil

+0

Nếu bạn đang gọi phương thức lớp của đối tượng, bạn có cần truyền đối tượng đó trong arg không? –

+4

Mặc dù nó được chấp nhận, câu trả lời này là tiếc là không phải là một mẫu tốt để gọi mã Python (và tôi đã nhìn thấy nó tham chiếu ở nơi khác). Dòng 'PyTuple_Pack' làm rò rỉ một đối tượng' float' mỗi khi nó được thực thi. Hàm này có thể được gọi mà không cần chuyển đổi thủ công và tạo tuple, với 'result = PyObject_CallFunction (myFunction," d ", 2.0)'. Việc nhập có thể được viết dưới dạng 'module = PyImport_ImportModule (" mytest ")' (không tạo chuỗi Python). – user4815162342

1

Bạn phải giải nén phương thức python bằng cách nào đó và chạy nó bằng PyObject_CallObject(). Để làm điều đó, bạn có thể cung cấp cho Python một cách để thiết lập chức năng, như ví dụ Extending and Embedding Python Tutorial.

6

Một ví dụ hoàn chỉnh gọi một hàm Python và lấy kết quả tọa lạc tại http://docs.python.org/release/2.6.5/extending/embedding.html#pure-embedding:

#include <Python.h> 

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

    if (argc < 3) { 
     fprintf(stderr,"Usage: call pythonfile funcname [args]\n"); 
     return 1; 
    } 

    Py_Initialize(); 
    pName = PyString_FromString(argv[1]); 
    /* Error checking of pName left out */ 

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

    if (pModule != NULL) { 
     pFunc = PyObject_GetAttrString(pModule, argv[2]); 
     /* pFunc is a new reference */ 

     if (pFunc && PyCallable_Check(pFunc)) { 
      pArgs = PyTuple_New(argc - 3); 
      for (i = 0; i < argc - 3; ++i) { 
       pValue = PyInt_FromLong(atoi(argv[i + 3])); 
       if (!pValue) { 
        Py_DECREF(pArgs); 
        Py_DECREF(pModule); 
        fprintf(stderr, "Cannot convert argument\n"); 
        return 1; 
       } 
       /* pValue reference stolen here: */ 
       PyTuple_SetItem(pArgs, i, pValue); 
      } 
      pValue = PyObject_CallObject(pFunc, pArgs); 
      Py_DECREF(pArgs); 
      if (pValue != NULL) { 
       printf("Result of call: %ld\n", PyInt_AsLong(pValue)); 
       Py_DECREF(pValue); 
      } 
      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 \"%s\"\n", argv[2]); 
     } 
     Py_XDECREF(pFunc); 
     Py_DECREF(pModule); 
    } 
    else { 
     PyErr_Print(); 
     fprintf(stderr, "Failed to load \"%s\"\n", argv[1]); 
     return 1; 
    } 
    Py_Finalize(); 
    return 0; 
} 
+0

Điều này làm việc trong 2 phút, cảm ơn rất nhiều! – jhegedus

1

Nếu bạn gán giá trị trả về cho một biến, sau đó bạn có thể sử dụng một cái gì đó giống như PyEval_GetGlobals() và PyDict_GetItemString () để lấy PyObject. Từ đó, PyNumber_Float có thể giúp bạn có được giá trị bạn muốn.

Tôi đề xuất duyệt qua toàn bộ API - một số điều trở nên rõ ràng khi bạn thấy các phương pháp khác nhau có sẵn cho bạn và có thể có phương pháp tốt hơn phương pháp tôi đã mô tả.

0

Tôi đã làm nó bằng cách sử BOOST để Python nhúng để C++ [mô-đun C làm việc này sẽ giúp]

#include <boost/python.hpp> 

void main() 
{ 
using namespace boost::python; 
Py_Initialize(); 
PyObject* filename = PyString_FromString((char*)"memory_leak_test"); 
    PyObject* imp = PyImport_Import(filename); 
    PyObject* func = PyObject_GetAttrString(imp,(char*)"begin"); 
    PyObject* args = PyTuple_Pack(1,PyString_FromString("CacheSetup")); 
    PyObject* retured_value = PyObject_CallObject(func, args); // if you have arg 
    double retured_value = PyFloat_AsDouble(myResult); 
std::cout << result << std::endl; 
Py_Finalize(); 
} 
+0

Phần tăng cường trong tập lệnh này ở đâu? – Antonello

12

Dưới đây là một số mẫu mã tôi đã viết (với sự giúp đỡ của các nguồn trực tuyến khác nhau) để gửi một chuỗi cho một mã Python, sau đó trả về một giá trị.

Đây là mã C call_function.c:

#include <Python.h> 
#include <stdlib.h> 
int main() 
{ 
    // Set PYTHONPATH TO working directory 
    setenv("PYTHONPATH",".",1); 

    PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *presult; 


    // Initialize the Python Interpreter 
    Py_Initialize(); 


    // Build the name object 
    pName = PyString_FromString((char*)"arbName"); 

    // Load the module object 
    pModule = PyImport_Import(pName); 


    // pDict is a borrowed reference 
    pDict = PyModule_GetDict(pModule); 


    // pFunc is also a borrowed reference 
    pFunc = PyDict_GetItemString(pDict, (char*)"someFunction"); 

    if (PyCallable_Check(pFunc)) 
    { 
     pValue=Py_BuildValue("(z)",(char*)"something"); 
     PyErr_Print(); 
     printf("Let's give this a shot!\n"); 
     presult=PyObject_CallObject(pFunc,pValue); 
     PyErr_Print(); 
    } else 
    { 
     PyErr_Print(); 
    } 
    printf("Result is %d\n",PyInt_AsLong(presult)); 
    Py_DECREF(pValue); 

    // Clean up 
    Py_DECREF(pModule); 
    Py_DECREF(pName); 

    // Finish the Python Interpreter 
    Py_Finalize(); 


    return 0; 
} 

Đây là mã Python, trong tập tin arbName.py:

def someFunction(text): 
    print 'You passed this Python program '+text+' from C! Congratulations!' 
    return 12345 

tôi sử dụng lệnh gcc call_function.c -I/usr/include/python2.6 -lpython2.6 ; ./a.out để chạy quá trình này. Tôi đang trên redhat. Tôi khuyên bạn nên sử dụng PyErr_Print(); để kiểm tra lỗi.

+0

@ Palec- Tôi không thể tìm ra những gì bạn thay đổi. Bạn có thể giúp tôi thấy sai lầm của tôi không? – kilojoules

+2

Gọi 'a.out' sẽ tốt hơn là'./A.out' vì lệnh gọi 'a.out' của bạn dựa vào thư mục làm việc đang ở trong PATH, đây không phải là cấu hình rất phổ biến. Bạn cũng nên xem xét việc cung cấp tùy chọn '-o' cho GCC để cung cấp cho tên thực thi tốt hơn. – Palec

+1

Xem [Cách chỉnh sửa công việc?] (Http://meta.stackexchange.com/a/21789/238706) trên Meta SE. Nếu bạn có thể truy cập lịch sử sửa đổi nhưng không thể thấy các thay đổi trong đánh dấu, chỉ cần chuyển chế độ xem khác thành 'đánh dấu song song'. Có các nút ở đầu mỗi bản sửa đổi trong lịch sử sửa đổi để chuyển đổi chế độ xem khác. Nếu bạn không hiểu đánh dấu là gì, hãy tham khảo [help help] (http://stackoverflow.com/editing-help). Nếu bạn đang chỉnh sửa bài đăng, trình chỉnh sửa có trợ giúp được tích hợp sẵn - hãy xem dấu chấm hỏi màu cam ở trên cùng bên phải của trình chỉnh sửa. – Palec

2

Để ngăn chặn thêm file py như trong câu trả lời khác, bạn chỉ có thể lấy các module __main__, được tạo ra bởi các cuộc gọi đầu tiên để PyRun_SimpleString:

PyObject *moduleMainString = PyString_FromString("__main__"); 
PyObject *moduleMain = PyImport_Import(moduleMainString); 

PyRun_SimpleString(
    "def mul(a, b):         \n"\ 
    " return a * b        \n"\ 
); 

PyObject *func = PyObject_GetAttrString(moduleMain, "mul"); 
PyObject *args = PyTuple_Pack(2, PyFloat_FromDouble(3.0), PyFloat_FromDouble(4.0)); 

PyObject *result = PyObject_CallObject(func, args); 

printf("mul(3,4): %.2f\n", PyFloat_AsDouble(result)); // 12 
Các vấn đề liên quan