{tp_alloc, tp_dealloc} 和 {tp_new, tp_free} 应该被视为成对吗?

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

在 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); };

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

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

并在 tp_dealloc

中摧毁它们

这是correct/optimal吗?

代码:

// 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:
    //    )
    //
    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 documentation你有

The tp_new function should call subtype->tp_alloc(subtype, nitems) to allocate space for the object, and then do only as much further initialization as is absolutely necessary. Initialization that can safely be ignored or repeated should be placed in the tp_init handler. A good rule of thumb is that for immutable types, all initialization should take place in tp_new, while for mutable types, most initialization should be deferred to tp_init.

这就是您在 tp_new 中创建对象本身并在 tp_init 中初始化它的原因。创建 C++ 对象是初始化的一部分。由于 tp_init documentation 状态

This function corresponds to the __init__() method of classes. Like __init__(), it is possible to create an instance without calling __init__(), and it is possible to reinitialize an instance by calling its __init__() method again.

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

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


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