2013-05-17 16 views
13

Tôi đang cố gắng gọi các phương thức trong một lớp python từ C++. Phương thức C++ mà từ đó được gọi là một cuộc gọi lại C++.Gọi phương thức python từ C++ (hoặc C) gọi lại

Trong phương pháp này khi tôi đang cố gắng gọi phương thức python, nó đã đưa ra segmentation fault.

tôi đã tiết kiệm được một thể hiện của chức năng python trong một biến toàn cầu như

// (pFunc is global variable of type PyObject*) 
pFunc = PyDict_GetItemString(pDict, "PlxMsgWrapper"); 

nơi PlxMsgWrapper là một phương pháp trăn, mà sẽ được sử dụng trong callback.

Trong callback, các đối số được tạo ra như

PyObject* args = PyTuple_Pack(2, PyString_FromString(header.c_str()), 
           PyString_FromString(payload.c_str())); 

Khi tạo

PyObject * pInstance = PyObject_CallObject(pFunc, args); 

Trong dòng này cho nó lỗi segmentation. Sau đó phương pháp python thực tế được gọi là

PyObject* recv_msg_func = PyObject_GetAttrString(module, (char *)"recvCallback"); 
args = PyTuple_Pack(1, pInstance); 
PyObject_CallObject(recv_msg_func, args); 
+0

Tôi đã cố gắng hết sức để làm sáng tỏ đúng cách.Vui lòng bình luận lại nếu có bất kỳ vấn đề gì. – Chaitanya

+0

Bạn đã kiểm tra xem PyObject_GetAttrString có thực sự trả về một cái gì đó có thể sử dụng không? Có lẽ việc tra cứu không thành công vì một lý do nào đó. Có lẽ 'module' không được khởi tạo đúng cách? – djf

+0

@djf Trên thực tế, bản thân kiểm soát không đến cho đến thời điểm đó. 'PyObject_CallObject (pFunc, args)'. Của nó đâm trong khi gọi phương pháp này chính nó. Để loại bỏ các vấn đề mô-đun, tôi không tải các vấn đề đó trong cuộc gọi lại. – Chaitanya

Trả lời

27

có một vài điều bạn cần phải làm gì nếu bạn là cách gọi một hàm Python từ một C/C++ gọi lại. Đầu tiên khi bạn lưu tắt đối tượng chức năng python của bạn, bạn cần phải làm tăng số lần tham khảo với:

Py_INCREF(pFunc) 

Nếu không Python không có ý tưởng bạn đang nắm giữ một tham chiếu đối tượng, và nó có thể rác thu thập nó, kết quả là một phân đoạn lỗi khi bạn cố gắng sử dụng nó từ cuộc gọi lại của bạn.

Sau đó, điều tiếp theo bạn cần quan tâm là chuỗi nào đang chạy khi gọi lại C/C++ của bạn được gọi. Nếu bạn nhận được gọi lại từ một luồng không được tạo Python khác (tức là một luồng C/C++ đang nhận dữ liệu trên một socket), thì bạn PHẢI mua Khóa thông dịch toàn cầu của Python (GIL) trước khi gọi bất kỳ hàm API Python nào. Nếu không, hành vi của chương trình là không xác định. Để có được những GIL bạn:

void callback() { 
    PyGILState_STATE gstate; 
    gstate = PyGILState_Ensure(); 

    // Get args, etc. 

    // Call your Python function object 
    PyObject * pInstance = PyObject_CallObject(pFunc, args); 

    // Do any other needed Python API operations 

    // Release the thread. No Python API allowed beyond this point. 
    PyGILState_Release(gstate); 
} 

Ngoài ra, trong hàm init mô-đun mở rộng của bạn, bạn nên làm như sau để đảm bảo luồng đó được khởi tạo đúng cách:

// Make sure the GIL has been created since we need to acquire it in our 
// callback to safely call into the python application. 
if (! PyEval_ThreadsInitialized()) { 
    PyEval_InitThreads(); 
} 

Nếu không, tai nạn và hành vi lạ có thể xảy ra khi bạn cố gắng lấy GIL từ một chuỗi không phải Python.

Xem Non-Python Created Threads để biết thêm chi tiết về điều này.

+1

Cảm ơn rất nhiều. :) Đó là vấn đề chính xác. – Chaitanya

+0

Cảm ơn rất nhiều - đã không được coi là tôi có thể đang ở trong chuỗi sai. – eddiewould

2

Python nên tìm kiếm một mô-đun trong thư mục nơi mà nó đang được chạy từ, tuy nhiên, nếu bạn nghĩ rằng vấn đề này là python là không tìm thấy tập tin của bạn , bạn có thể thêm một thư mục tùy ý trên máy tính của bạn vào đường dẫn tìm kiếm mô-đun trong chương trình của bạn:

// Initialize the Python Interpreter 
Py_Initialize(); 

// The following two lines to the trick: 
// add path to your module to python's search paths 
PyRun_SimpleString("import sys"); 
PyRun_SimpleString("sys.path.append(\"/path/to/python/module/here\")"); 

// Build the name object 
pName = PyString_FromString("your_module"); 

// 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, "PlxMsgWrapper"); 

pArgs = ... 

if (PyCallable_Check(pFunc)) 
{ 
    PyObject_CallObject(pFunc, pArgs); 
} else { 
    PyErr_Print(); 
} 
2

Điều này không trả lời chính xác câu hỏi của bạn, nhưng bạn có thể đơn giản hóa mã của mình và tránh các vấn đề về số tham chiếu với Boost::Python.

#include "boost/python.hpp" 

using namespace boost::python; 

int main() 
{ 
    Py_Initialize(); 

    object pyFunPlxMsgWrapper = import("your_module").attr("PlxMsgWrapper"); 
    pyFunPlxMsgWrapper(2, "string", "data"); 
    return 0; 
}