2009-02-21 8 views
5

Desidero utilizzare gli operatori new ed delete per creare e distruggere i miei oggetti.Python C-API Object Allocation

Il problema è che Python sembra suddividerlo in più fasi. tp_new, tp_init e tp_alloc per la creazione e tp_del, tp_free e tp_dealloc per la distruzione. Comunque C++ ha solo una nuova che alloca e costruisce completamente l'oggetto ed elimina quali distruggono e rilasciano l'oggetto.

Quali dei metodi python tp_ * devo fornire e cosa devono fare?

Anche io voglio essere in grado di creare l'oggetto direttamente in C++ ad esempio "PyObject * obj = new MyExtensionObject (args);" Dovrò anche sovraccaricare il nuovo operatore in qualche modo per supportare questo?

Mi piacerebbe anche essere in grado di sottoclassi i miei tipi di estensione in python, c'è qualcosa di speciale che devo fare per supportare questo?

Sto usando python 3.0.1.

EDIT: ok, tp_init sembra rendere gli oggetti un po 'troppo mutevole per quello che sto facendo (ad esempio, prendere un oggetto Texture, cambiando il contenuto dopo la creazione va bene, ma il cambiamento aspetti fondamentali di essa, come, dimensioni, bitdept, etc interromperà molte cose C++ esistenti che presuppongono che questo tipo di cose siano corrette). Se non lo implementerò, semplicemente smetterà di chiamare __init__ DOPO che è stato costruito (o almeno ignora la chiamata, come fa la tupla). O dovrei avere qualche bandiera che genera un'eccezione o qualcosa se tp_init viene chiamato più volte sullo stesso oggetto?

A parte ciò, credo che abbiate risolto la maggior parte degli altri.

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(); 
    } 
    ... 
}; 

risposta

11

La documentazione di questi è a http://docs.python.org/3.0/c-api/typeobj.html e http://docs.python.org/3.0/extending/newtypes.html si descrive come eseguire il proprio tipo.

tp_alloc esegue l'allocazione di memoria di basso livello per l'istanza. Questo è equivalente a malloc(), più inizializza il refcnt a 1. Python ha il proprio allocatore, PyType_GenericAlloc, ma un tipo può implementare un allocatore specializzato.

tp_new è uguale a __new__ di Python. Di solito viene utilizzato per oggetti immutabili in cui i dati vengono archiviati nell'istanza stessa, rispetto a un puntatore ai dati. Ad esempio, stringhe e tuple memorizzano i loro dati nell'istanza, invece di usare un char * o un PyTuple *.

In questo caso, tp_new calcola quanta memoria è necessaria, in base ai parametri di input, e chiama tp_alloc per ottenere la memoria, quindi inizializza i campi essenziali. tp_new non ha bisogno di chiamare tp_alloc. Ad esempio, può restituire un oggetto memorizzato nella cache.

tp_init è uguale a __init__ di Python. La maggior parte della tua inizializzazione dovrebbe essere in questa funzione.

La distinzione tra __new__ e __init__ è denominata two-stage initialization o two-phase initialization.

Si dice "C++ ha appena il nuovo" ma non è corretto. tp_alloc corrisponde un allocatore di arena personalizzato in C++, __new__ corrisponde ad un allocatore di tipo personalizzato (una funzione di fabbrica), e __init__ è più simile al costruttore. Quest'ultimo link discute di più sui paralleli tra lo stile C++ e Python.

Leggere anche http://www.python.org/download/releases/2.2/descrintro/ per dettagli su come __new__ e __init__ interagiscono.

Si scrive che si desidera "creare l'oggetto direttamente in C++". Questo è piuttosto difficile perché almeno dovrai convertire tutte le eccezioni Python che si sono verificate durante l'istanza degli oggetti in un'eccezione C++. Potresti provare a cercare Boost :: Python per un po 'di aiuto con questa attività. O puoi usare un'inizializzazione a due fasi. ;)

0

non so le API di Python a tutti, ma se pitone divide l'allocazione e l'inizializzazione, si dovrebbe essere in grado di utilizzare nuova collocazione.

es .:

// 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

Il posizionamento 'new' sarebbe intuitivo, ma purtroppo' new (p) Class (args); 'esegue l'inizializzazione zero prima di richiamare il costruttore e che cancella i valori che il codice di allocazione/inizializzazione python ha inserito nell'oggetto memoria (ad es. il conteggio dei riferimenti). Separatamente, usando 'new char [n]' quindi le strutture sovrapposte sono pericolose perché l'allineamento di memoria garantito è solo 1. –