Должны ли {tp_alloc, tp_dealloc} и {tp_new, tp_free} рассматриваться как пары?

Правда ли, что все, что создано в 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++ отдельно друг от друга и поэтому работает по-другому.

Он создает PyObject в tp_new и соответствующий объект C++ в tp_init.

И уничтожает их обоих в 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://stackru.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);
}

1 ответ

Решение

От 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_alloc в tp_new а также tp_free в tp_dealloc, Так {tp_alloc, tp_free} а также {tp_new, tp_dealloc} следует рассматривать как пары.

Другие вопросы по тегам