Производные от произвольного Python класса с C API и `tp_basicsize` для` object`? - PullRequest
0 голосов
/ 19 марта 2020

Я пытаюсь определить функцию, которая создала бы класс Python с использованием C API, который происходит от произвольного Python типа base и имеет дополнительное поле void* my_ptr в своем необработанном C -подобное расположение объектов. Я хочу, чтобы он повторно использовал Python __dict__ функциональность.

Я не делаю это в C, поэтому не имею доступа к C макросам. Моя первоначальная попытка выглядит следующим образом (псевдокод):

PyType Derive(PyType base) {
  var newType = new PyType(...);

  newType.tp_flags = HeapType | BaseType; // <- this is important,
    // one should be able to add new attributes and otherwise use __dict__, subclass, etc

  ... filling in other things ...

  int my_ptr_offset = base.tp_basesize; // put my_ptr immediately after base type data
  newType.tp_basesize = my_ptr_offset + sizeof(void*); // instances of new type
    // will have instance size = base size + size of my_ptr

  ...

  return newType;
}

Проблема в том, что этот код ломается, когда base равен builtins.object. В этом случае tp_basesize не считает поле, в котором обычно хранится __dict__, а my_ptr_offset в конечном итоге указывает на это поле, что в конечном итоге приводит к его перезаписи пользователем my_ptr.

* 1016. * Любой простой Python класс, производный от object, не имеет этой проблемы. Например:
class MySimpleClass: pass

На 64-битной машине:

PyType mySimpleClass = ...;
PyType object = ...;
mySimpleClass.tp_basesize // <- 32, includes __dict__
object.tp_basesize // <- 16, does not include space for __dict__

Я также заметил похожую проблему с builtins.exception.

Сейчас я просто вручную проверяю exception и object и добавьте 2x sizeof(void*) к tp_basesize, что, кажется, работает. Но я хотел бы понять, как правильно обрабатывать этот макет.

1 Ответ

0 голосов
/ 25 марта 2020

Я думаю, что информация, которую вы хотите, находится в tp_dictoffset базы. Если для этого параметра установлено значение 0, то у базы нет __dict__, чего-либо еще, и у него есть.

Мне немного неясно, как вы создаете свои типы, но по крайней мере через вызов PyType_Type (метод, используемый внутренне при написании class X: в Python), добавляется dict, если не определено __slots__ - звучит так, будто это то, что вы хотите, и то, что происходит. Это подробно описано в разделе «Наследование» в разделе документации, который я связал.

Поэтому, если tp_dictoffset == 0 (и предполагается, что вы не определяете __slots__), тогда добавьте sizeof(PyObject*) для учета словаря, который неявно добавлено.

...