Правда ли, что все, что создано в 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://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
документация состояния
Эта функция соответствует __в этом__() метод классов. подобно __в этом__(), можно создать экземпляр без вызова __в этом__(), и можно повторно инициализировать экземпляр, вызвав его __в этом__() метод снова.
Вам нужно проверить 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
} следует рассматривать как пары.