我正致力于桥接C ++和Python。
当创建我的自定义类型的新实例时,我需要将某些C ++实例方法注册为正在创建的Python对象的属性。
相关代码流程如下:
// during setup, we fill slots for the underlying PyTypeObject
p = new wrapperFor_PyTypeObject{ sizeof(FinalClass), 0, default_name };
p->set_tp_new( extension_object_new );
p->set_tp_init( extension_object_init );
p->set_tp_dealloc( extension_object_deallocator );
:
感兴趣的是set_tp_init
static int extension_object_init( PyObject* _self, PyObject* _args, PyObject* _kwds )
{
try
{
Py::Tuple args{_args};
Py::Dict kwds = _kwds ? Py::Dict{_kwds} : Py::Dict{};
PythonClassInstance* self{ reinterpret_cast<PythonClassInstance*>(_self) };
if( self->m_pycxx_object )
{
self->m_pycxx_object->reinit( args, kwds );
DBG_PRINT( "reinit -" );
}
else
self->m_pycxx_object = new FinalClass{ self, args, kwds };
// here we force all c++ relevant class instance methods to register as attributes
FinalClass* f = (FinalClass*)(self->m_pycxx_object);
f->AddAll();
}
catch(...)
:
void AddAll()
{
auto& methods = FuncMapper<FinalClass>::methods();
//auto py_method_table = new PyMethodDef[ methods.size() + 1 ]{}; // sentinel must be 0'd, which it is thx to {}
for( auto& m : methods )
{
PyObject* a{ selfPtr() }; // 'this' class derives from PyObject
支持PyObject * const char * str = m.first.c_str();
Object callable{ m.second->ConstructCFunc(this) }; // ConstructCFunc uses PyCFunction_New
callable.increment_reference_count();
PyObject* c{ callable.ptr() }; // extract backing PyObject* pointer
int ret = PyObject_SetAttrString( a, str, c );
if( ret == -1 )
throw AttributeError{ m.first };
}
}
我已经以一种非常迂腐的方式声明了所有变量,以确保它们正确地传递到PyObject_SetAttrString,它看起来就是这样。
但是,PyObject_SetAttrString返回-1(错误)。
查看CPython源代码,我无法确定此错误的来源:
int
PyObject_SetAttrString(PyObject *v, const char *name, PyObject *w)
{
PyObject *s;
int res;
if (Py_TYPE(v)->tp_setattr != NULL)
return (*Py_TYPE(v)->tp_setattr)(v, (char*)name, w);
s = PyUnicode_InternFromString(name);
if (s == NULL)
return -1;
res = PyObject_SetAttr(v, s, w);
Py_XDECREF(s);
return res;
}
int
PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value)
{
PyTypeObject *tp = Py_TYPE(v);
int err;
if (!PyUnicode_Check(name)) {
PyErr_Format(PyExc_TypeError,
"attribute name must be string, not '%.200s'",
name->ob_type->tp_name);
return -1;
}
Py_INCREF(name);
PyUnicode_InternInPlace(&name);
if (tp->tp_setattro != NULL) {
err = (*tp->tp_setattro)(v, name, value); // <-- SHOULD HIT HERE
Py_DECREF(name);
return err;
}
if (tp->tp_setattr != NULL) {
char *name_str = _PyUnicode_AsString(name);
if (name_str == NULL)
return -1;
err = (*tp->tp_setattr)(v, name_str, value);
Py_DECREF(name);
return err;
}
Py_DECREF(name);
assert(name->ob_refcnt >= 1);
if (tp->tp_getattr == NULL && tp->tp_getattro == NULL)
PyErr_Format(PyExc_TypeError,
"'%.100s' object has no attributes "
"(%s .%U)",
tp->tp_name,
value==NULL ? "del" : "assign to",
name);
else
PyErr_Format(PyExc_TypeError,
"'%.100s' object has only read-only attributes "
"(%s .%U)",
tp->tp_name,
value==NULL ? "del" : "assign to",
name);
return -1;
}
有什么我可以尝试将CPython源添加到我的项目并单步执行它吗?
通过查看输入的值,它应该达到我标记的点:
PyUnicode_InternInPlace(&name);
if (tp->tp_setattro != NULL) {
err = (*tp->tp_setattro)(v, name, value); // <-- SHOULD HIT HERE
Py_DECREF(name);
return err;
}
但是我无法看到如何调试到tp_setattro。它是函数表中的一个槽。浏览源代码会显示大量访问。