2010-10-30 2 views
1

Я пишу неизменный класс связанных списков в C, но один из методов таинственно segfault. Код предназначен для примерно эквивалентно следующему:Почему этот метод 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) 

Вот мой код:

#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); 
} 

Если я запускаю этот код, он возвращает ошибку сегментации на последней строке:

from pysistence.persistent_list import PList  

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

Я уверен, что я просто делаю что-то глупое, но я не понимаю, что это такое. Есть что-то, что мне не хватает?

+0

Просто для справки (это гораздо позже): код также имеет refcounting ошибку. Вы не должны вызывать 'Py_INCREF()' в 'PList_cons()'. –

ответ

4

Я читаю из документации:


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

вызова вызываемым объекта Python вызываемого с переменным числом аргументов PyObject *. Аргументы приводятся как переменное количество параметров, за которыми следует NULL. Возвращает результат успешного вызова или NULL при сбое.


В конце отсутствует значение NULL.

Edit: Ho и вы также хотите, чтобы проверить, если функция возвращает NULL в случае отказа памяти

+0

Doh! Понимаете, я сказал, что это было что-то глупое. :-) –

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