2009-02-21 2 views
5

Я хочу использовать новые и удалять операторы для создания и уничтожения моих объектов.Распределение объектов Python C-API

Проблема заключается в том, что python разбивает его на несколько этапов. tp_new, tp_init и tp_alloc для создания и tp_del, tp_free и tp_dealloc для уничтожения. Однако C++ имеет только новый, который выделяет и полностью конструирует объект и удаляет, который разрушает и освобождает объект.

Какой из методов python tp_ * необходимо предоставить и что они должны делать?

Также я хочу, чтобы иметь возможность создать объект непосредственно в C++, например, «PyObject» obj = new MyExtensionObject (args); » Должен ли я также перегрузить новый оператор каким-то образом, чтобы поддержать это?

Я также хотел бы иметь возможность подклассифицировать мои типы расширений в python, есть ли что-то специальное, что мне нужно сделать, чтобы поддержать это?

Я использую python 3.0.1.

EDIT: ok, tp_init, похоже, делает объекты слишком чересчурными для того, что я делаю (например, возьмите объект Texture, изменив содержимое после его создания, но измените его фундаментальные аспекты, такие как размер, битдепт и т. д. будет ломать множество существующих материалов C++, которые предполагают, что такие вещи фиксированы). Если я не реализую его, это просто остановит людей, вызывающих __init__ ПОСЛЕ создания (или, по крайней мере, игнорирования вызова, например, кортежа). Или должен ли я иметь некоторый флаг, который генерирует исключение или somthing, если tp_init вызывается более одного раза на одном и том же объекте?

Помимо этого, я думаю, что у меня большая часть остального отсортирована.

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

ответ

11

документация для них в http://docs.python.org/3.0/c-api/typeobj.html и http://docs.python.org/3.0/extending/newtypes.html описывает, как сделать свой собственный тип.

tp_alloc выполняет низкоуровневое выделение памяти для экземпляра. Это эквивалентно malloc(), плюс инициализирует refcnt до 1. Python имеет собственный распределитель PyType_GenericAlloc, но тип может реализовать специализированный распределитель.

tp_new - это то же, что и __new__ Python. Он обычно используется для неизменяемых объектов, где данные хранятся в самом экземпляре, по сравнению с указателем на данные. Например, строки и кортежи хранят свои данные в экземпляре, вместо использования char * или PyTuple *.

Для этого случая tp_new вычисляет, сколько памяти требуется на основе входных параметров и вызывает tp_alloc для получения памяти, а затем инициализирует основные поля. tp_new не нужно вызывать tp_alloc. Например, он может возвращать кешированный объект.

tp_init - это то же, что и __init__ Python. Большая часть вашей инициализации должна быть в этой функции.

Различие между __new__ и __init__ называется two-stage initialization, или two-phase initialization.

Вы говорите: «C++ имеет только новый», но это неверно. tp_alloc соответствует произвольному распределению арены в C++, __new__ соответствует специализированному распределителю типов (заводская функция), а __init__ больше похож на конструктор. В этой последней ссылке больше обсуждается параллели между стилем C++ и Python.

Также читайте http://www.python.org/download/releases/2.2/descrintro/, чтобы узнать, как взаимодействуют __new__ и __init__.

Вы пишете, что хотите «создать объект непосредственно в C++». Это довольно сложно, потому что по крайней мере вам придется преобразовать любые исключения Python, которые произошли во время создания объекта, в исключение C++. Вы можете попытаться найти Boost :: Python для некоторой помощи в выполнении этой задачи. Или вы можете использовать двухфазную инициализацию. ;)

0

Я не знаю API, питона вообще, но если питон распадается распределения и инициализации, вы должны быть в состоянии использовать размещение новый.

.: например

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

Размещение 'new' было бы интуитивно понятным, но, к сожалению,' new (p) Class (args); 'выполняет инициализацию нуля перед вызовом конструктора и устраняет значения, которые код размещения/инициализации python помещает в объект памяти (например, счетчик ссылок). Отдельно использование 'new char [n]' тогда накладываемых структур опасно, потому что гарантированное выравнивание памяти составляет всего 1. –

Смежные вопросы