2010-10-30 5 views
1

J'écris une classe de liste liée immuable en C, mais une méthode est mystérieusement segfaulting. Le code est destiné à être à peu près équivalent à ceci:Pourquoi cette méthode C segfaulting?

class PList(object): 
    def __init__(self, first, rest=None): 
     self.first = first 
     self.rest = rest 

    def cons(self, item): 
     return PList(item, self) 

Voici mon code:

#include <Python.h> 
#include <structmember.h> 

static PyTypeObject PListType; 

typedef struct PListStruct{ 
    PyObject_HEAD 
    PyObject *first; 
    struct PListStruct *rest; 
} PList; 

static PyMemberDef plist_members[] = { 
    {"first", T_OBJECT_EX, offsetof(PList, first), READONLY, "First element"}, 
    {"rest", T_OBJECT_EX, offsetof(PList, rest), READONLY, "Rest of the list"}, 
    {NULL} 
}; 

static PyObject * 
PList_cons(PList *self, PyObject *arg) 
{ 
    PList *new_list = PyObject_CallFunctionObjArgs(&PListType, arg, self); 
    Py_INCREF(new_list); 
    return new_list; 
} 

static PyMethodDef plist_methods[] = { 
    {"cons", (PyCFunction)PList_cons, METH_O, "Add an item to the list"}, 
    {NULL} 
}; 


static void PList_dealloc(PList *self) 
{ 
    Py_XDECREF(self->first); 
    Py_XDECREF(self->rest); 
    self->ob_type->tp_free((PyObject*)self); 
} 

static int 
PList_init(PList *self, PyObject *args, PyObject *kwds) 
{ 
    PyObject *first=NULL, *rest=NULL, *tmp; 

    static char *kwlist[] = {"first", "rest", NULL}; 
    if (! PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, 
            &first, &rest)) 
    return -1; 

    if (first){ 
    tmp = self->first; 
    Py_INCREF(first); 
    self->first = first; 
    Py_XDECREF(tmp); 
    } 

    if (rest) { 
    tmp = self->rest; 
    Py_INCREF(rest); 
    self->rest = rest; 
    Py_XDECREF(tmp); 
    } 
    else { 
    tmp = self->rest; 
    Py_INCREF(Py_None); 
    self->rest = Py_None; 
    Py_XDECREF(tmp); 
    } 

    return 0; 
} 

static PyTypeObject PListType= { 
    PyObject_HEAD_INIT(NULL) 
    0,       /*ob_size*/ 
    "pysistence.persistent_list.PList",    /*tp_name*/ 
    sizeof(PList), /*tp_basicsize*/ 
    0,       /*tp_itemsize*/ 
    (destructor)PList_dealloc,       /*tp_dealloc*/ 
    0,       /*tp_print*/ 
    0,       /*tp_getattr*/ 
    0,       /*tp_setattr*/ 
    0,       /*tp_compare*/ 
    0,       /*tp_repr*/ 
    0,       /*tp_as_number*/ 
    0,       /*tp_as_sequence*/ 
    0,       /*tp_as_mapping*/ 
    0,       /*tp_hash */ 
    0,       /*tp_call*/ 
    0,       /*tp_str*/ 
    0,       /*tp_getattro*/ 
    0,       /*tp_setattro*/ 
    0,       /*tp_as_buffer*/ 
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /*tp_flags*/ 
    "Persistent list",   /* tp_doc */ 
    0,      /* tp_traverse */ 
    0,      /* tp_clear */ 
    0,      /* tp_richcompare */ 
    0,      /* tp_weaklistoffset */ 
    0,      /* tp_iter */ 
    0,      /* tp_iternext */ 
    plist_methods,   /* tp_methods */ 
    plist_members,      /* tp_members */ 
    0,      /* tp_getset */ 
    0,      /* tp_base */ 
    0,      /* tp_dict */ 
    0,      /* tp_descr_get */ 
    0,      /* tp_descr_set */ 
    0,      /* tp_dictoffset */ 
    (initproc)PList_init, /* tp_init */ 
    0,      /* tp_alloc */ 
    0,      /* tp_new */ 
}; 

#ifndef PyMODINIT_FUNC 
#define PyMODINIT_FUNC void 
#endif 

PyMODINIT_FUNC 
initpersistent_list(void) 
{ 
    PyObject *m; 

    PListType.tp_new = PyType_GenericNew; 
    if (PyType_Ready(&PListType) < 0) 
    return; 

    m = Py_InitModule3("pysistence.persistent_list", 0, 
        "Docstring"); 
    Py_INCREF(&PListType); 
    PyModule_AddObject(m, "PList", (PyObject*)&PListType); 
} 

Si je lance ce code, il segfaults sur la dernière ligne:

from pysistence.persistent_list import PList  

p = PList(1) 
p = PList(2, p) 
p = p.cons(3) 

Je suis sûr que je fais juste quelque chose de stupide, mais je ne vois pas ce que c'est. Y a-t-il quelque chose qui me manque?

+0

Juste pour référence (il est beaucoup plus tard): le code a aussi un bug refcounting. Vous ne devriez pas appeler 'Py_INCREF()' dans 'PList_cons()'. –

Répondre

4

Je lis la documentation:


PyObject* PyObject_CallFunctionObjArgs(PyObject *callable, ..., NULL) 
    Return value: New reference. 

Appeler un objet Python appelable appelable, avec un nombre variable d'arguments PyObject *. Les arguments sont fournis sous la forme d'un nombre variable de paramètres suivi de NULL. Retourne le résultat de l'appel en cas de succès, ou NULL en cas d'échec.


Il vous manque la valeur NULL à la fin.

Edit: Ho et vous voulez également vérifier si la fonction retourne NULL en cas d'échec de la mémoire

+0

Doh! Tu vois, j'ai dit que c'était quelque chose de stupide. :-) –

Questions connexes