2009-02-21 11 views
5

Ich möchte die neuen und löschenden Operatoren zum Erstellen und Zerstören meiner Objekte verwenden.Python C-API Objekt Zuweisung

Das Problem ist Python scheint es in mehrere Stufen zu brechen. tp_new, tp_init und tp_alloc für die Erstellung und tp_del, tp_free und tp_dealloc für die Zerstörung. Allerdings hat C++ nur neue, die das Objekt zuweist und vollständig konstruiert und löscht, was das Objekt destruiert und freigibt.

Welche der python tp_ * Methoden muss ich bereitstellen und was müssen sie tun?

Auch ich möchte in der Lage sein, das Objekt direkt in C++ zB "PyObject * obj = new MyExtensionObject (args);" Muss ich den neuen Betreiber auch irgendwie überlasten, um dies zu unterstützen?

Ich möchte auch in der Lage sein, meine Erweiterungstypen in Python Unterklasse, gibt es etwas Besonderes, das ich tun muss, um dies zu unterstützen?

Ich benutze Python 3.0.1.

EDIT: ok, tp_init scheint Objekte etwas zu veränderlich zu machen für das, was ich mache (zB ein Textur-Objekt nehmen, den Inhalt nach der Erstellung ändern, aber grundlegende Aspekte ändern, wie Größe, bitdept, etc. wird viele existierende C++ - Sachen zerstören, die davon ausgehen, dass diese Dinge behoben sind. Wenn ich es nicht implementiere, wird es einfach Leute stoppen, die __init__ NACH ihrer Konstruktion aufrufen (oder zumindest den Aufruf ignorieren, wie Tupel das tut). Oder sollte ich ein Flag haben, das eine Exception oder etwas auslöst, wenn tp_init mehr als einmal für dasselbe Objekt aufgerufen wird?

Abgesehen davon glaube ich, dass ive den Rest größtenteils sortiert hatte.

extern "C" 
{ 
    //creation + destruction 
    PyObject* global_alloc(PyTypeObject *type, Py_ssize_t items) 
    { 
     return (PyObject*)new char[type->tp_basicsize + items*type->tp_itemsize]; 
    } 
    void global_free(void *mem) 
    { 
     delete[] (char*)mem; 
    } 
} 
template<class T> class ExtensionType 
{ 
    PyTypeObject *t; 
    ExtensionType() 
    { 
     t = new PyTypeObject();//not sure on this one, what is the "correct" way to create an empty type object 
     memset((void*)t, 0, sizeof(PyTypeObject)); 
     static PyVarObject init = {PyObject_HEAD_INIT, 0}; 
     *((PyObject*)t) = init; 

     t->tp_basicsize = sizeof(T); 
     t->tp_itemsize = 0; 

     t->tp_name = "unknown"; 

     t->tp_alloc = (allocfunc) global_alloc; 
     t->tp_free = (freefunc) global_free; 
     t->tp_new  = (newfunc) T::obj_new; 
     t->tp_dealloc = (destructor)T::obj_dealloc; 
     ... 
    } 
    ...bunch of methods for changing stuff... 
    PyObject *Finalise() 
    { 
    ... 
    } 
}; 
template <class T> PyObjectExtension : public PyObject 
{ 
... 
    extern "C" static PyObject* obj_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) 
    { 
     void *mem = (void*)subtype->tp_alloc(subtype, 0); 
     return (PyObject*)new(mem) T(args, kwds) 
    } 
    extern "C" static void obj_dealloc(PyObject *obj) 
    { 
     ~T(); 
     obj->ob_type->tp_free(obj);//most of the time this is global_free(obj) 
    } 
... 
}; 
class MyObject : PyObjectExtension<MyObject> 
{ 
public: 
    static PyObject* InitType() 
    { 
     ExtensionType<MyObject> extType(); 
     ...sets other stuff... 
     return extType.Finalise(); 
    } 
    ... 
}; 

Antwort

11

Die Dokumentation für diese ist bei http://docs.python.org/3.0/c-api/typeobj.html und http://docs.python.org/3.0/extending/newtypes.html beschreibt, wie Sie Ihre eigene Art zu machen.

tp_alloc führt die Low-Level-Speicherzuweisung für die Instanz durch. Dies entspricht malloc() und initialisiert die refcnt auf 1. Python hat seinen eigenen Zuordner, PyType_GenericAlloc, aber ein Typ kann einen spezialisierten Zuordner implementieren.

tp_new ist das gleiche wie Pythons __new__. Es wird normalerweise für unveränderliche Objekte verwendet, in denen die Daten in der Instanz selbst gespeichert werden, im Vergleich zu einem Zeiger auf Daten. Beispielsweise speichern Strings und Tupel ihre Daten in der Instanz, anstatt ein char * oder ein PyTuple * zu verwenden.

In diesem Fall ermittelt tp_new basierend auf den Eingabeparametern, wie viel Speicher benötigt wird, und ruft tp_alloc auf, um den Speicher abzurufen, und initialisiert dann die wesentlichen Felder. tp_new muss tp_alloc nicht aufrufen. Es kann beispielsweise ein zwischengespeichertes Objekt zurückgeben.

tp_init ist das gleiche wie Pythons __init__. Der Großteil Ihrer Initialisierung sollte in dieser Funktion sein. Die Unterscheidung zwischen __new__ und __init__ heißt two-stage initialization oder two-phase initialization.

Sie sagen, „C++ nur neue hat“, aber das ist nicht richtig. tp_alloc entspricht einem benutzerdefinierten Arena-Zuordner in C++, __new__ entspricht einem benutzerdefinierten Typzuordner (einer Factory-Funktion), und __init__ ähnelt eher dem Konstruktor. Dieser letzte Link diskutiert mehr über die Parallelen zwischen C++ und Python-Stil.

Lesen Sie auch http://www.python.org/download/releases/2.2/descrintro/ für Einzelheiten darüber, wie __new__ und __init__ interagieren.

Sie schreiben, dass Sie zu wollen, „das Objekt direkt in C++ erstellen“. Das ist ziemlich schwierig, denn zumindest müssen Sie alle Python-Ausnahmen, die während der Objekt-Instanziierung aufgetreten sind, in eine C++ - Ausnahme konvertieren. Sie können versuchen, Boost :: Python für einige Hilfe bei dieser Aufgabe zu suchen. Oder Sie können eine zweiphasige Initialisierung verwenden. ;)

0

Ich weiß nicht, den Python-APIs überhaupt, aber wenn Python auf Zuweisung und Initialisierung teilt, sollen Sie in der Lage sein, die Platzierung zu nutzen, um neue.

z.B .:

// tp_alloc 
void *buffer = new char[sizeof(MyExtensionObject)]; 
// tp_init or tp_new (not sure what the distinction is there) 
new (buffer) MyExtensionObject(args); 
return static_cast<MyExtensionObject*>(buffer); 

... 
// tp_del 
myExtensionObject->~MyExtensionObject(); // call dtor 
// tp_dealloc (or tp_free? again I don't know the python apis) 
delete [] (static_cast<char*>(static_cast<void*>(myExtensionObject))); 
+1

Platzierung 'neu' wäre intuitiv, aber leider' neu (p) Klasse (args); 'führt vor dem Aufruf des Konstruktors eine Null-Initialisierung durch und löscht Werte, die der Python-Zuordnungs/Initialisierungs-Code in das Objekt geschrieben hat Speicher (z. B. die Referenzzählung). Getrennt davon ist die Verwendung von 'new char [n]' dann überlagernden Strukturen gefährlich, da die garantierte Speicherausrichtung nur 1 ist. –

Verwandte Themen