2009-02-21 21 views
5

Chcę użyć operatorów nowych i usuwania do tworzenia i niszczenia moich obiektów.Python C-API Przydział obiektów

Problem polega na tym, że pyton dzieli go na kilka etapów. tp_new, tp_init i tp_alloc do stworzenia i tp_del, tp_free i tp_dealloc do zniszczenia. Jednak C++ ma tylko nowe, które przydziela i w pełni konstruuje obiekt i usuwa go, który niszczy i zwalnia obiekt.

Które z metod python tp_ * muszę podać i co należy zrobić?

Chcę również móc utworzyć obiekt bezpośrednio w języku C++, np. "PyObject * obj = new MyExtensionObject (args);" Czy będę musiał w jakiś sposób przeciążyć nowego operatora, aby wesprzeć to?

Chciałbym również móc podklasować moje typy rozszerzeń w python, czy jest coś specjalnego, co muszę zrobić, aby to wspierać?

Używam Pythona 3.0.1.

EDYTOWANIE: ok, tp_init sprawia, że ​​obiekty są zbyt zmienne w stosunku do tego, co robię (np. Weź obiekt Tekstury, zmieniając zawartość po utworzeniu jest w porządku, ale zmieniaj jej podstawowe aspekty, takie jak rozmiar, bitdept, itp. zepsuje wiele istniejących rzeczy w języku C++, które zakładają, że tego typu rzeczy są naprawione). Jeśli tego nie zrobię, to po prostu przestanę dzwonić __init__ PO jego skonstruowaniu (lub przynajmniej zignorować połączenie, tak jak robi to krotka). Czy powinienem mieć flagę, która rzuca wyjątek lub coś, jeśli tp_init jest wywoływany więcej niż jeden raz na tym samym obiekcie?

Poza tym myślę, że resztę posortowałem.

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

Odpowiedz

11

Dokumentacja dla nich jest w http://docs.python.org/3.0/c-api/typeobj.html i http://docs.python.org/3.0/extending/newtypes.html opisuje jak zrobić własny typ.

tp_alloc wykonuje przydział pamięci niskiego poziomu dla instancji. Jest to odpowiednik malloc(), a także zainicjować refcnt na 1. Python ma swój własny przydział, PyType_GenericAlloc, ale typ może zaimplementować wyspecjalizowany alokator.

tp_new to to samo co __new__ w Pythonie. Jest zwykle używany do niezmiennych obiektów, w których dane są przechowywane w samej instancji, w porównaniu do wskaźnika do danych. Na przykład ciągi i krotki przechowują swoje dane w instancji, zamiast używać znaku * lub PyTuple *.

W tym przypadku tp_new określa, ile pamięci jest potrzebne, w oparciu o parametry wejściowe, i wywołuje tp_alloc w celu pobrania pamięci, a następnie inicjuje podstawowe pola. tp_new nie musi wywoływać tp_alloc. Może na przykład zwrócić buforowany obiekt.

tp_init to to samo co __init__ w Pythonie. Większość twoich inicjalizacji powinna znajdować się w tej funkcji.

Rozróżnienie między __new__ a __init__ nazywa się two-stage initialization lub two-phase initialization.

Mówisz "C++ właśnie ma nowy" ale to nie jest poprawne. tp_alloc odpowiada niestandardowemu alokatorowi aren w C++, __new__ odpowiada niestandardowemu alokatorowi typu (funkcja fabryczna), a __init__ bardziej przypomina konstruktor. Ten ostatni link omawia więcej paraleli między C++ i stylem Python.

Przeczytaj również http://www.python.org/download/releases/2.2/descrintro/, aby uzyskać szczegółowe informacje na temat interakcji __new__ i __init__.

Piszesz, że chcesz "stworzyć obiekt bezpośrednio w C++". Jest to dość trudne, ponieważ przynajmniej będziesz musiał przekonwertować wszystkie wyjątki Pythona, które wystąpiły podczas tworzenia obiektów w wyjątek C++. Możesz spróbować spojrzeć na Boost :: Python, aby uzyskać pomoc w tym zadaniu. Lub możesz użyć inicjalizacji dwufazowej. ;)

0

Nie wiem API Pythona w ogóle, ale jeśli pyton dzieli się alokacji i inicjalizacji, powinieneś być w stanie korzystać umieszczenie nowego.

np .:

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

Umieszczenie 'new' byłoby intuicyjne, ale niestety' new (p) Class (args); 'wykonuje zerową inicjalizację przed wywołaniem konstruktora, a to usuwa wartości, które przydzielił kod Pythona do alokacji/inicjalizacji do obiektu pamięć (np. liczba referencyjna). Oddzielnie, używając 'new char [n]', wtedy nakładanie struktur jest niebezpieczne, ponieważ gwarantowane wyrównanie pamięci wynosi tylko 1. –