In C müssen wir selbst aufräumen:
static void looker_destroy(lookerobject *self) { munmap(self->g_front, self->g_back-self->g_front); close(self->fd); if (self->comparbuf) { free(self->comparbuf); } PyObject_Del(self); }
Das Ganze muss natürlich noch ein Modul werden:
static PyObject *looker_new(PyObject *self, PyObject *args) { char *fname; if (!PyArg_ParseTuple(args, "s", &fname)) { return NULL; } return fillLookerObjectStruct(fname); } static PyMethodDef lookerMethods[] = { {"Looker", looker_new, METH_VARARGS, "Creates a new looker instance."}, {NULL, NULL, 0, NULL}, }; void initLooker(void) { Py_InitModule("Looker", lookerMethods); }
Der Konstruktor steht hier also etwas allein, nämlich als Funktion im Modul und nicht als Methode der Klasse. Das muss auch mehr oder weniger so sein, weil sein Name nach außen sichtbar ist.
Die Funktion fillLookerObjectStruct, die wir im Konstruktor verwenden, ist eine relativ langweilige Hilfsfunktion, die Teile der alten Initialisierung verwendet und ansonsten die früher globalen Variablen setzt:
static PyObject *fillLookerObjectStruct(char *fname) { struct stat sb; lookerobject *self; self = PyObject_New(lookerobject, &Lookertype); if (!self) { /* If this fails, we're too hosed to even bother */ return NULL; /* setting an error string */ } if ((self->fd = open(fname, O_RDONLY, 0)) < 0 | fstat(self->fd, &sb)) { PyErr_SetFromErrno(PyExc_IOError); return NULL; } if ((void *)(self->g_front = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_FILE|MAP_SHARED, self->fd, (off_t)0)) <= (void *)0) { PyErr_SetFromErrno(PyExc_IOError); return NULL; } self->g_back = self->g_front + sb.st_size; self->dflag = 0; self->fflag = 1; self->comparbuf = NULL; self->string = NULL; self->searchcount = 0; return (PyObject*)self; }
Um das Ergebnis als String zurückzugeben (Wunsch 2), müssen wir die alte print_from-Funktion verändern, etwa so:
static PyObject* collect_from(lookerobject *self, char *front, char *back) { int eol; char *strbeg; strbeg = front; while (front < back && compare(self, front, back, self->fflag) == EQUAL) { if (compare(self, front, back, self->fflag) == EQUAL) { eol = 0; while (front < back && !eol) { if (*front++ == '\n') eol = 1; } } else SKIP_PAST_NEWLINE(front, back); } return PyString_FromStringAndSize(strbeg, front-strbeg); }
Damit müssen natürlich auch die darunterliegenden Funktionen etwas verändert werden. Ihr findet den Quelltext im Anhang dieser Seite.
In collect_from packen wir einfach alles, was früher ausgegeben wurde, in einen Python-String. Dazu müssen wir noch nicht mal verstanden haben, was eigentlich alles im look vor sich geht.
In der Tat ist das auch etwas verwickelt, wie ihr seht, wenn ihr die (guten) Kommentare im Original-Quelltext lest. Für Interessierte hier dennoch der grobe Ablauf:
Wenn man lange genug darüber nachdenkt, findet man, dass das äußere if überflüssig ist, weil seine Bedingung nie falsch werden kann (das stimmt aber auch nur, weil compare keine Seiteneffekte hat). Vermutlich war hier irgendwann mal anderer Code, mit dem zusammen die Abfrage Sinn hatte. Merke: Auch veröffentlichter Code ist nicht immer perfekt.
Wenn ihr den Eindruck habt, dass es relativ haarig ist, Python-Erweiterungen in C zu schreiben, so liegt ihr richtig. Das haben sich auch andere Menschen gedacht. In der Realität wir man darum in der Regel Hilfsmittel verwenden, die die Erzeugung von solchen Erweiterungen leichter machen. Dazu gehört z.B. SWIG, das beim Schreiben von “wrappern” um bestehende C- oder C++-Bibliotheken hilft, oder Pyrex, das die Grenze zwischen Python und C recht erfolgreich verwischt. Googelt danach.