Распределение объектов Python C-API - PullRequest
7 голосов
/ 21 февраля 2009

Я хочу использовать операторы new и delete для создания и уничтожения моих объектов.

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

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

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

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

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

EDIT: Хорошо, tp_init, кажется, делает объекты немного изменчивыми для того, что я делаю (например, возьмите объект Texture, измените содержимое после создания, все в порядке, но изменение его фундаментальных аспектов, таких как, размер, bitdept и т. д. сломает много существующий материал C ++, который предполагает, что такие вещи исправлены). Если я не реализую его, он просто перестанет вызывать __init__ ПОСЛЕ его создания (или, по крайней мере, проигнорирует вызов, как это делает кортеж). Или у меня должен быть какой-нибудь флаг, который выдает исключение или что-то, если 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();
    }
    ...
};

Ответы [ 2 ]

11 голосов
/ 21 февраля 2009

Документация для них на 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__ называется двухэтапная инициализация , или двухфазная инициализация .

Вы говорите: " 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 голосов
/ 21 февраля 2009

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

например:.

 // 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)));
...