PEP 3123 – Making PyObject_HEAD conform to standard C
- Autor:
- Martin von Löwis <martin at v.loewis.de>
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 27-Apr-2007
- Python-Version:
- 3.0
- Post-History:
Inhaltsverzeichnis
Zusammenfassung
Python verlässt sich derzeit auf undefiniertes C-Verhalten mit seiner Verwendung von PyObject_HEAD. Dieser PEP schlägt vor, dies in Standard-C zu ändern.
Begründung
Standard-C definiert, dass ein Objekt nur über einen Zeiger seines Typs zugegriffen werden darf und dass alle anderen Zugriffe undefiniertes Verhalten sind, mit wenigen Ausnahmen. Insbesondere der folgende Code weist undefiniertes Verhalten auf
struct FooObject{
PyObject_HEAD
int data;
};
PyObject *foo(struct FooObject*f){
return (PyObject*)f;
}
int bar(){
struct FooObject *f = malloc(sizeof(struct FooObject));
struct PyObject *o = foo(f);
f->ob_refcnt = 0;
o->ob_refcnt = 1;
return f->ob_refcnt;
}
Das Problem hierbei ist, dass der Speicher sowohl als struct PyObject als auch als struct FooObject angesprochen wird.
Historisch hatten Compiler keine Probleme mit diesem Code. Moderne Compiler nutzen diese Klausel jedoch als Optimierungsmöglichkeit. Sie stellen fest, dass f->ob_refcnt und o->ob_refcnt unmöglich auf denselben Speicher verweisen können und dass die Funktion daher 0 zurückgeben sollte, ohne den Wert von ob_refcnt überhaupt in der return-Anweisung abrufen zu müssen. Für GCC verwendet Python nun -fno-strict-aliasing als Workaround für dieses Problem; mit anderen Compilern kann es einfach zu undefiniertem Verhalten kommen. Selbst mit GCC kann die Verwendung von -fno-strict-aliasing den generierten Code unnötig verlangsamen.
Spezifikation
Standard-C hat eine spezifische Ausnahme von seinen Aliasing-Regeln, die genau dafür konzipiert ist, den Fall von Python zu unterstützen: Ein Wert eines Struct-Typs darf auch über einen Zeiger auf das erste Feld zugegriffen werden. Zum Beispiel, wenn ein Struct mit einem int beginnt, kann der struct * auch in einen int * gecastet werden, was das Schreiben von Integer-Werten in das erste Feld ermöglicht.
Für Python werden PyObject_HEAD und PyObject_VAR_HEAD so geändert, dass sie nicht mehr alle Felder auflisten, sondern ein einzelnes Feld vom Typ PyObject/PyVarObject auflisten.
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
typedef struct {
PyObject ob_base;
Py_ssize_t ob_size;
} PyVarObject;
#define PyObject_HEAD PyObject ob_base;
#define PyObject_VAR_HEAD PyVarObject ob_base;
Typen, die als fest dimensionierte Struktur definiert sind, enthalten dann PyObject als erstes Feld, PyVarObject für variable Objekte. Z.B.
typedef struct {
PyObject ob_base;
PyObject *start, *stop, *step;
} PySliceObject;
typedef struct {
PyVarObject ob_base;
PyObject **ob_item;
Py_ssize_t allocated;
} PyListObject;
Die obigen Definitionen von PyObject_HEAD sind maßgeblich, daher KÖNNEN Erweiterungsautoren entweder das Makro verwenden oder das Feld ob_base explizit in ihre Structs einfügen.
Als Konvention sollte das Basis-Feld ob_base genannt werden. Alle Zugriffe auf ob_refcnt und ob_type MÜSSEN jedoch den Objektzeiger nach PyObject* casten (es sei denn, der Zeiger hat bereits diesen Typ) und SOLLTEN die jeweiligen Accessor-Makros verwenden. Um den Zugriff auf ob_type, ob_refcnt und ob_size zu vereinfachen, werden Makros
#define Py_TYPE(o) (((PyObject*)(o))->ob_type)
#define Py_REFCNT(o) (((PyObject*)(o))->ob_refcnt)
#define Py_SIZE(o) (((PyVarObject*)(o))->ob_size)
hinzugefügt. Z.B. die Codeblöcke
#define PyList_CheckExact(op) ((op)->ob_type == &PyList_Type)
return func->ob_type->tp_name;
müssen geändert werden zu
#define PyList_CheckExact(op) (Py_TYPE(op) == &PyList_Type)
return Py_TYPE(func)->tp_name;
Für die Initialisierung von Typobjekten ist die aktuelle Sequenz
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
nicht mehr korrekt und muss ersetzt werden durch
PyVarObject_HEAD_INIT(NULL, 0)
Kompatibilität mit Python 2.6
Um Module zu unterstützen, die sowohl mit Python 2.6 als auch mit Python 3.0 kompiliert werden, werden die Py_* Makros zu Python 2.6 hinzugefügt. Die Makros Py_INCREF und Py_DECREF werden geändert, um ihr Argument nach PyObject * zu casten, so dass Modulautoren auch das Feld ob_base explizit in Modulen deklarieren können, die für Python 2.6 entwickelt wurden.
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-3123.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT