使用具有C庫的eGige相機的控件工作時,我開始了一個cython代碼project,它帶有每種語言最好的東西。使用python-c-api調用的Cython回調段錯誤
該庫提供了一種方法來偵聽來自相機的心跳,以瞭解它是否已斷開連接。在C++類中的回調函數我已經做了,但是從這個C++類中調用一個類的python方法在我試過的所有方法中都陷入了分段錯誤。
我封裝它在一個特定的C++類:
#include <Python.h>
/* (...) */
PyCallback::PyCallback(PyObject* self, const char* methodName)
{
Py_XINCREF(self);
_self = self;
_method = PyObject_GetAttrString(self, methodName);
}
PyCallback::~PyCallback()
{
Py_XDECREF(_self);
}
void PyCallback::execute()
{
try
{
PyObject *args = PyTuple_Pack(1,_self);
PyObject_CallFunctionObjArgs(_method, args);
}catch(...){
_error("Exception calling python");
}
}
從用Cython對象的代碼是:
cdef class Camera(...):
# (...)
cdef registerRemovalCallback(self):
cdef:
PyCallback* obj
obj = new PyCallback(<PyObject*> self, <char*> "cameraRemovalCallback")
cdef cameraRemovalCallback(self):
self._isPresent = False
回溯的最低層,它只是在嘗試準備參數。
#0 0x00007ffff7b24592 in PyErr_Restore() from /usr/lib64/libpython2.6.so.1.0
#1 0x00007ffff7b23fef in PyErr_SetString() from /usr/lib64/libpython2.6.so.1.0
#2 0x00007ffff7b314dd in ??() from /usr/lib64/libpython2.6.so.1.0
#3 0x00007ffff7b313ca in ??() from /usr/lib64/libpython2.6.so.1.0
#4 0x00007ffff7b316c1 in ??() from /usr/lib64/libpython2.6.so.1.0
#5 0x00007ffff7b31d2f in ??() from /usr/lib64/libpython2.6.so.1.0
#6 0x00007ffff7b31e9c in Py_BuildValue() from /usr/lib64/libpython2.6.so.1.0
#7 0x00007ffff637cbf8 in PyCallback::execute (this=0x16212a0) at pylon/PyCallback.cpp:53
#8 0x00007ffff6376248 in CppCamera::removalCallback (this=0x161fb30, pDevice=<value optimized out>) at pylon/Camera.cpp:387
我試圖讓使用_Py_BuildValue( 「(自我)」,個體經營)的參數;但是我在那裏有段錯誤。
我也試圖與PyObject_CallFunctionObjArgs與在參數字段NULL,心想也許是指向「自我」已經被嵌入的方法指向一個特定的地址與此對象。但他們我在那裏有段錯誤。
有人看到我的錯誤嗎?那裏有什麼東西會以不同的方式做出來?我希望這是我身邊的誤解,關於誰來做這件事。
更新 @2016年8月1日:
發表意見之後適應症,兩處修改是在代碼所做的:
的所有指針存儲到PyCallback首先被存儲作爲相機的成員 cython級別:
cdef class Camera(...):
cdef:
#(...)
PyCallback* _cbObj
# (...)
cdef registerRemovalCallback(self):
self._cbObj = new PyCallback(<PyObject*> self, <char*> "cameraRemovalCallback")
cdef cameraRemovalCallback(self):
self._isPresent = False
即使這是segfaults的基本來源它看起來並沒有涉及當前的一個。
然後PyCallback :: execute()在C++中,我做了一些改變。閱讀有關GIL(全局解釋器鎖),併爲其添加幾個電話之後,我添加了一個檢查,可能引導到解決方案:
PyCallback::PyCallback(PyObject* self, const char* methodName)
{
Py_Initialize();
Py_XINCREF(self);
_self = self;
_method = PyObject_GetAttrString(self, methodName);
}
PyCallback::~PyCallback()
{
Py_XDECREF(_self);
Py_Finalize();
}
void PyCallback::execute()
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
try
{
if (PyCallable_Check(_method))
{
_info("Build arguments and call method");
PyObject *args = Py_BuildValue("(O)", _self);
PyObject *kwargs = Py_BuildValue("{}", "", NULL);
PyObject_Call(_method, args, kwargs);
}
else
{
_warning("The given method is not callable!");
}
}
catch(...)
{
// TODO: collect and show more information about the exception
_error("Exception calling python");
}
PyGILState_Release(gstate);
}
即使我不知道該怎麼辦了呼叫,重點是_PyCallable_Check_返回false。
我還測試了使用的typedef選項,Ç函數指針具有相同段錯誤結果來調用它。
更新 @2016年8月3日:
我繼續進行建議的修改。現在將cameraRemovalCallback
從cdef
更改爲def
,並且PyCallback
中的一些if
報告現在可以找到該方法。在~PyCallback()
中也加入了Py_XDECREF(_method)
,以防在構造函數中找到它。無用的try-catch
也被刪除。
從參考Python's Object protocol,那DavidW提到,我檢查了*Call*
組合的許多:落入段錯誤。
我認爲這個問題正在成爲髒,並得到一個論壇的外觀(question->答案 - >回放 - > ...)。對此我很抱歉,下次我會寫,告訴段錯誤已經解決,而且我會盡力。
回調函數可以用cython代碼實現。請參閱[回調示例](https://github.com/cython/cython/blob/master/Demos/callback/cheese.pyx),[一箇舊問題](http://stackoverflow.com/questions/5242051/ cython-implementation-callbacks)和[一個老問題](http://stackoverflow.com/questions/11700501/python-cython-c-and-callbacks-calling-a-python-function-from-c-using-用Cython)。 –
'PyObject_CallFunctionObjArgs'的文檔https://docs.python.org/2/c-api/object.html#c.PyObject_CallFunctionObjArgs意味着你應該傳遞一個可變數目的'PyObject *'s,然後是'NULL'。 'NULL'非常重要,因爲它告訴Python args列表已被覆蓋例如'PyObject_CallFunctionObjArgs(_method,self,NULL);' – DavidW
不幸的是,你的例子不夠完整,不足以說明這是唯一的問題。 (另外:至少在提供的代碼中,'obj'實際上並不保存在'registerRemovalCallback'中的任何地方。) – DavidW