2009-02-21 7 views
5

Quiero utilizar los operadores nuevos y eliminar para crear y destruir mis objetos.Asignación de objeto Python C-API

El problema es que Python parece dividirlo en varias etapas. tp_new, tp_init y tp_alloc para la creación y tp_del, tp_free y tp_dealloc para la destrucción. Sin embargo, C++ solo tiene una nueva que asigna y construye completamente el objeto y elimina lo que destruye y desasigna el objeto.

¿Cuál de los métodos python tp_ * debo proporcionar y qué deben hacer?

También quiero ser capaz de crear el objeto directamente en C++, por ejemplo, "PyObject * obj = new MyExtensionObject (args);" ¿También necesitaré sobrecargar al nuevo operador de alguna manera para apoyar esto?

También me gustaría poder subclasificar mis tipos de extensión en python, ¿hay algo especial que deba hacer para apoyar esto?

Estoy usando python 3.0.1.

EDIT: ok, tp_init parece hacer los objetos un poco demasiado mutables para lo que estoy haciendo (por ejemplo, tomar un objeto de textura, cambiar los contenidos después de la creación está bien, pero cambiar aspectos fundamentales como tamaño, bitdept, etc romperá muchas cosas de C++ existentes que asumen que ese tipo de cosas son fijas). Si no lo implemento, simplemente detendrá a las personas que llaman __init__ DESPUÉS de que se construya (o al menos ignore la llamada, como lo hace tuple). ¿O debería tener alguna bandera que arroje una excepción o algo si tp_init se invoca más de una vez en el mismo objeto?

Aparte de eso creo que he conseguido la mayor parte del resto ordenado.

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

Respuesta

11

La documentación de estos es en http://docs.python.org/3.0/c-api/typeobj.html y http://docs.python.org/3.0/extending/newtypes.html describe cómo hacer su propio tipo.

tp_alloc realiza la asignación de memoria de bajo nivel para la instancia. Esto es equivalente a malloc(), además de inicializar el refcnt a 1. Python tiene su propio asignador, PyType_GenericAlloc, pero un tipo puede implementar un asignador especializado.

tp_new es lo mismo que Python's __new__. Por lo general, se utiliza para objetos inmutables donde los datos se almacenan en la instancia en sí, en comparación con un puntero a los datos. Por ejemplo, las cadenas y las tuplas almacenan sus datos en la instancia, en lugar de utilizar un char * o un PyTuple *.

En este caso, tp_new averigua cuánta memoria se necesita, en función de los parámetros de entrada, y llama a tp_alloc para obtener la memoria, luego inicializa los campos esenciales. tp_new no necesita llamar a tp_alloc. Por ejemplo, puede devolver un objeto en caché.

tp_init es lo mismo que __init__ de Python. La mayor parte de su inicialización debe estar en esta función.

La distinción entre __new__ y __init__ se llama two-stage initialization, o two-phase initialization.

Usted dice "C++ simplemente tiene nuevo" pero eso no es correcto. tp_alloc corresponde a un asignador de arena personalizado en C++, __new__ corresponde a un asignador de tipo personalizado (una función de fábrica), y __init__ es más parecido al constructor. Ese último enlace analiza más sobre los paralelismos entre C++ y el estilo de Python.

Lea también http://www.python.org/download/releases/2.2/descrintro/ para obtener detalles sobre cómo __new__ y __init__ interactúan.

Usted escribe que quiere "crear el objeto directamente en C++". Eso es bastante difícil porque, como mínimo, tendrá que convertir cualquier excepción de Python que haya ocurrido durante la instanciación de objetos en una excepción de C++. Puede intentar consultar Boost :: Python para obtener ayuda con esta tarea. O puede usar una inicialización de dos fases. ;)

0

no sé las API de Python en absoluto, pero si pitón divide la asignación e inicialización, usted debería ser capaz de utilizar la colocación de nuevo.

ej .:

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

La ubicación 'new' sería intuitiva, pero lamentablemente' new (p) Class (args); 'realiza la inicialización cero antes de invocar el constructor y borra los valores que el código de asignación/inicialización de python ha puesto en el objeto memoria (por ejemplo, el recuento de referencia). Por separado, usar 'new char [n]' y superponer estructuras es peligroso porque la alineación de memoria garantizada es solo 1. –

Cuestiones relacionadas