在 Python 中,使用 C-API 进行对象分配时,需要考虑 tp_* 方法的使用方式。这些方法用于创建和销毁对象,包括 tp_new、tp_init 和 tp_alloc 用于创建,而 tp_del、tp_free 和 tp_dealloc 用于销毁。而在 C++ 中,可以使用 new 来分配和构造对象,同时使用 delete 来析构和释放对象。
2、解决方案
对于 Python 中的对象分配,需要提供相应的 tp_* 方法。
1)tp_alloc:
该方法用于分配对象的内存空间。它类似于 malloc(),但会将引用计数初始化为 1。Python 提供了自己的分配器 PyType_GenericAlloc,但类型也可以实现自己的专门分配器。
2)tp_new:
该方法与 Python 中的 new 方法相同。通常用于不可变对象,其中数据存储在实例本身,而不是数据指针。例如,字符串和元组将数据存储在实例中,而不是使用 char * 或 PyTuple *。在 tp_new 中,需要根据输入参数计算所需的内存量,然后调用 tp_alloc 获取内存,之后再初始化基本字段。该方法不需要调用 tp_alloc,也可以返回缓存对象。
3)tp_init:
该方法与 Python 中的 init 方法相同。大部分的初始化操作都应在此函数中进行。
4)其他方法:
除了上述方法外,还需要实现 tp_free 和 tp_dealloc 方法来释放对象占用的内存空间。
5)针对特定需求的解决方法:
对于希望直接在 C++ 中创建对象的情况,由于需要将 Python 异常转换为 C++ 异常,因此实现起来较为复杂。可以选择使用 Boost::Python 来简化这一过程。另外,也可以使用两阶段初始化来完成。
6)代码例子:
extern "C"
{
//creation + destruction
PyObject* global_alloc(PyTypeObject *type, Py_ssize_t items)
{
return (PyObject*)new char[type->tp_basicsize + items*type->tp_itemsize];
}
void global_free(void *mem)
{
delete[] (char*)mem;
}
}
template<class T> class ExtensionType
{
PyTypeObject *t;
ExtensionType()
{
t = new PyTypeObject();//not sure on this one, what is the "correct" way to create an empty type object
memset((void*)t, 0, sizeof(PyTypeObject));
static PyVarObject init = {PyObject_HEAD_INIT, 0};
*((PyObject*)t) = init;
t->tp_basicsize = sizeof(T);
t->tp_itemsize = 0;
t->tp_name = "unknown";
t->tp_alloc = (allocfunc) global_alloc;
t->tp_free = (freefunc) global_free;
t->tp_new = (newfunc) T::obj_new;
t->tp_dealloc = (destructor)T::obj_dealloc;
...
}
...bunch of methods for changing stuff...
PyObject *Finalise()
{
...
}
};
template <class T> PyObjectExtension : public PyObject
{
...
extern "C" static PyObject* obj_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
{
void *mem = (void*)subtype->tp_alloc(subtype, 0);
return (PyObject*)new(mem) T(args, kwds)
}
extern "C" static void obj_dealloc(PyObject *obj)
{
~T();
obj->ob_type->tp_free(obj);//most of the time this is global_free(obj)
}
...
};
class MyObject : PyObjectExtension<MyObject>
{
public:
static PyObject* InitType()
{
ExtensionType<MyObject> extType();
...sets other stuff...
return extType.Finalise();
}
...
};