Pregunta

Estoy escribiendo una clase de lista enlazada inmutables en C, pero un método es misteriosamente violación de segmento. El código está destinado a ser más o menos equivalente a esto:

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

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

Aquí está mi código:

#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 ejecuta este código, se segfaults en la última línea:

from pysistence.persistent_list import PList    

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

estoy seguro de que simplemente estoy haciendo algo estúpido, pero no veo lo que es. ¿Hay algo que estoy perdiendo?

¿Fue útil?

Solución

Estoy leyendo a partir de la documentación:


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

Llamar a un objeto invocable exigible Python, con un número variable de argumentos PyObject *. Los argumentos se proporcionan como un número variable de parámetros seguido por NULL. Devuelve el resultado de la llamada en caso de éxito, o NULL en caso de error.


se echa en falta el valor nulo al final.

Editar: Ho y también quieren comprobar si el NULL devuelve la función en caso de fallo de memoria

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top