使用具有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