views:

93

answers:

1

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?

+3  A: 

I am reading from the documentation:


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

Call a callable Python object callable, with a variable number of PyObject* arguments. The arguments are provided as a variable number of parameters followed by NULL. Returns the result of the call on success, or NULL on failure.


You are missing the NULL value at the end.

Edit: Ho and you also want to check if the function returns NULL in case of memory failure

Nicolas Viennot
Doh! See, I said it was something stupid. :-)
Jason Baker