I'm writing an immutable linked list class in C, but one method is mysteriously segfaulting. The code is intended to be roughly equivalent to this:
class PList(object):
def __init__(self, first, rest=None):
self.first = first
self.rest = rest
def cons(self, item):
return PList(item, self)
Here is my 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);
}
If I run this code, it segfaults on the last line:
from pysistence.persistent_list import PList
p = PList(1)
p = PList(2, p)
p = p.cons(3)
I'm sure I'm just doing something stupid, but I don't see what it is. Is there something I'm missing?