{tp_alloc, tp_dealloc} 和 {tp_new, tp_free} 是否应被视为对

Should {tp_alloc, tp_dealloc} and {tp_new, tp_free} be considered as pairs?

本文关键字:tp alloc dealloc new free 是否      更新时间:2023-10-16

tp_alloc中创建的任何内容都应该在tp_dealloc中销毁吗? 同样,对于{tp_new,tp_free}?

它看起来像一个明显的对称性,但我希望得到澄清。


我的实际用例是这样的:我有:

class OSClass : PyObject {...}
class Final : OSClass {...}

所以相应的PyTypeObject pto有:

pto->tp_basicsize = sizeof(FinalClass)
pto->tp_dealloc = (destructor) 
                  [](PyObject* pyob) { PyMem_Free(pyob); };

但是,新的样式类将 PyObject 及其相应的 C++ 对象彼此分开存储,因此工作方式不同。

它在tp_new中创建 PyObject,在tp_init中创建相应的 C++ 对象。

并在tp_dealloc中摧毁了他们两个

这是正确/最佳的吗?

法典:

// extra void* to point to corresponding C++ object
pto->tp_basicsize = sizeof(PyObject) + sizeof(void*)
pto->tp_new = new_func;
pto->tp_init = init_func;
pto->tp_dealloc = dealloc_func;
static PyObject* new_func( PyTypeObject* subtype, PyObject* args, PyObject* kwds )
{
    // First we create the Python object.
    // The type-object's tp_basicsize is set to sizeof(Bridge)
    // (Note: We could maybe use PyType_GenericNew for this:
    //   http://stackoverflow.com/questions/573275/python-c-api-object-allocation )
    //
    PyObject* pyob = subtype->tp_alloc(subtype,0);
    Bridge* bridge = reinterpret_cast<Bridge*>(pyob);
    // We construct the C++ object later in init_func (below)
    bridge->m_pycxx_object = nullptr;
    return pyob;
}

static int init_func( PyObject* self, PyObject* args, PyObject* kwds )
{
    try
    {
        Object a = to_tuple(args);
        Object k = to_dict(kwds);
        Bridge* bridge{ reinterpret_cast<Bridge*>(self) };
        // NOTE: observe this is where we invoke the 
        //       constructor, but indirectly (i.e. through final)
        bridge->m_pycxx_object = new FinalClass{ bridge, a, k };
    }
    catch( Exception & )
    {
        return -1;
    }
    return 0;
}
static void dealloc_func( PyObject* pyob )
{
    auto final = static_cast<FinalClass*>( cxxbase_for(pyob) );
    delete final;
    PyMem_Free(pyob);
    COUT( "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" );
    //self->ob_type->tp_free(self);
}

从您拥有的tp_new文档中

tp_new函数应调用 subtype->tp_alloc(subtype, nitems) 为对象分配空间,然后仅执行绝对必要的进一步初始化。可以安全忽略或重复的初始化应放在tp_init处理程序中。一个好的经验法则是,对于不可变类型,所有初始化都应在tp_new中进行,而对于可变类型,大多数初始化应推迟到tp_init

这就是为什么您在 tp_new 中创建对象本身并在 tp_init 中初始化它的原因。创建C++对象是初始化的一部分。由于tp_init文档指出

此函数对应于类的 __init__() 方法。与 __init__() 一样,可以在不调用 __init__() 的情况下创建实例,并且可以通过再次调用其 __init__() 方法来重新初始化实例。

您需要检查bridge->m_pycxx_object != nullptr并在失败或引发错误时删除已初始化的实例。

tp_dealloc中,您将销毁 Python 对象。由于C++对象是此对象的一部分,因此也需要在那里销毁它。


回到配对:您在tp_new内调用tp_alloc,在tp_dealloc内调用tp_free。所以 { tp_alloctp_free } 和 { tp_newtp_dealloc } 应该被视为成对。