Frage zu konvertierung von ctype in Python Typ

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
snow
User
Beiträge: 25
Registriert: Mittwoch 4. Juli 2012, 08:52

Also muss ich dann mithilfe von ctypes den Speicher vom char array, welchen ich von der C Bibliothek bekomme, wieder frei machen, wenn ich diesen nicht mehr brauche?
BlackJack

@snow: Ja. Wobei man da üblicherweise in der Bibliothek eine Funktion für schreibt. Mit einem einfachen `free()` ist es ja nicht getan, man muss ja alle Zeichenketten in dem Array auch freigeben.
snow
User
Beiträge: 25
Registriert: Mittwoch 4. Juli 2012, 08:52

Ach ich Idiot. Ja die habe ich auch geschrieben. Keine Ahnung wieso ich da jetzt nicht dran gedacht habe die einfach aus Python heraus aufzurufen :D Danke dir :)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Zwei Hinweise hab ich noch bezüglich der VLAs und Deinem kurzen Codebsp.:

Wenn Du das plattformübergreifend halten willst und später mit dem MS-C Compiler übersetzen musst, fliegen Dir die VLAs um die Ohren. Der MS-Compiler ist auf C89 hängengeblieben und Du musst dann alle VLAs mit _alloca umschreiben. Das ist sehr nervig beim Refactoring.

Da Dir die Geschwindigkeit so wichtig ist, warum übergibst Du nicht `text` mit Offset und Länge dem `setInsert`? Das char cur[] ist imho ein überflüssige Kopieraktion.
snow
User
Beiträge: 25
Registriert: Mittwoch 4. Juli 2012, 08:52

Ich arbeite gerade nur auf Linux, aber ich wollte es auf jeden Fall auch unter Windows weiterhin lauffähig halten. Hatte eigentlich vor mit gcc für windows die Bibliothek zu erzeugen. Wird das ebenfalls zu Problemen führen/ geht das garnicht/ oder gibt das keine Probleme?

Das andere werd ich mal ändern, danke.
snow
User
Beiträge: 25
Registriert: Mittwoch 4. Juli 2012, 08:52

Ich habe das gerade mal geändert, dass ich nach dem auslesen, den Speicherplatz mit der Methode frei mache:

Code: Alles auswählen

pointer = self.lib.tokenize(c_char_p(text))
....
self.lib.dArrayFree(pointer)
und bekomme dann folgenden Fehler:

Code: Alles auswählen

*** Error in `/usr/bin/python': double free or corruption (out): 0x0000000002e84440 ***
Dann habe ich mal das in C probiert und habe den selben Fehler bekommen:

Code: Alles auswählen

    DArray *result = tokenize("$hallo wer @ist da");
    .....
    dArrayFree(result);
    dArrayFree(result);
Bei einem AUfruf an der Funktion im C Code wird das DArray ganz normal gecleared, aber beim zweiten mal crasht es dann wie im Python Code. Weiß jemand den Grund dafür?
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Naja, `free()` sollte üblicherweise auch nur exakt einmal pro Zeiger aufgerufen werden. Danach kann man nicht mehr davon ausgehen, dass der Zeiger noch gültig ist und somit kracht es beim zweiten Aufruf dann entsprechend.

Du sprichst hier von "gecleared". Ich würde bei "free" im Namen einer Funktion aber erwarten, dass da mehr gemacht wird, als nur den Inhalt zu leeren. Wie gesagt: "free" meint eher, dass das gebundene Objekt komplett abgeräumt werden soll, weil man es nicht mehr benutzen muss und somit der vom Objekt genutzte Speicher wieder freigegeben weden kann.
snow
User
Beiträge: 25
Registriert: Mittwoch 4. Juli 2012, 08:52

Ja aber im obigen Python Code rufe ich die free Methode ja nur einmal auf und es crasht genauso wie wenn ich es 2 mal im C Code aufrufen würde. Aber ich hab im Python Code wirklich nur einmal versucht den Speicher dafür zu leeren. Deswegen die Frage : Wieso crasht der Python Code als wenn ich 2 mal free aufgerufen hätte?
BlackJack

@snow: Kannst Du eingrenzen *wo* der Crash auftritt? Also vor oder nach dem Aufruf Deiner `dArrayFree()`-Funktion?

Ansonsten wäre ein minimales aber lauffähiges Beispiel praktisch, an dem man das nachvollziehen kann.
snow
User
Beiträge: 25
Registriert: Mittwoch 4. Juli 2012, 08:52

Der Crash tritt direkt beim Aufruf der dArrayFree Funktion auf.

Der gesamte Code ist dieser hier:

Code: Alles auswählen

class DArray(Structure):
    _fields_ = [("length", c_uint),
                ("capacity", c_uint),
                ("elems", POINTER(c_char_p))]


class RaschLexer():

    def __init__(self):
        self.lib = CDLL(./libSnowEdit.so")

        self.lib.tokenize.argtypes = [c_char_p]
        self.lib.tokenize.restype = POINTER(DArray)

        self.lib.dArrayFree.argtypes = [POINTER(DArray)]

    def getVariables(self, text):

        variables = set([])

        pointer = self.lib.tokenize(c_char_p(text))
        ret = pointer.contents
        for i in range(ret.length):
            variables.update([ret.elems[i]])

        self.lib.dArrayFree(pointer)

        return variables

Was vielleicht noch interessant ist, ist die C Funktion für den Free, aber ich glaube nicht dass es daran liegt, weil im C Code funktioniert sie:

Code: Alles auswählen

void dArrayFree(DArray *arr) {
    if (arr != NULL) {
        if (arr->elems != NULL){
            uint i;
            for (i = 0; i < arr->length; i++) {
                free(arr->elems[i]);
            }
            free(arr->elems);
        }
        free(arr);
    }
}
BlackJack

@snow: So auf Anhieb fällt mir eigentlich nur auf, dass der Rückgabetyp von der Funktion nicht auf `None` gesetzt wird. Damit ist nicht 100%ig ausgeschlossen, dass nach der Funktion nicht ein C-``int`` vom Aufrufstapel als Rückgabewert entfernt wird, der dann dort fehlt und zu Folgefehlern führt.

Die Klasse macht übrigens keinen Sinn. Der gemeinsame Zustand sind einfach nur ein paar Funktionen die man genauso gut auf Modulebene hätte definieren können und dann halt eine Lexer-/Parsefunktion. Ich hätte das so geschrieben (natürlich ungetestet):

Code: Alles auswählen

from itertools import islice


class DArray(Structure):
    _fields_ = [
        ('length', c_uint),
        ('capacity', c_uint),
        ('elems', POINTER(c_char_p))
    ]

D_ARRAY_P = POINTER(DArray)


lib_snow_edit = CDLL('./libSnowEdit.so')

_tokenize = lib_snow_edit.tokenize
_tokenize.argtypes = [c_char_p]
_tokenize.restype = D_ARRAY_P

_d_array_free = lib_snow_edit.dArrayFree
_d_array_free.argtypes = [D_ARRAY_P]
_d_array_free.restype = None


def rasch_parse(text):
    d_array_p = _tokenize(text)
    try:
        d_array = d_array_p.contents
        return set(islice(d_array, d_array.length))
    finally:
        _d_array_free(d_array_p)
snow
User
Beiträge: 25
Registriert: Mittwoch 4. Juli 2012, 08:52

Habs mal geändert.Aber leider hat das Hinzufügen von None als restype keinen Einfluss gehabt und es taucht immer noch der selbe Fehler auf :(
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@snow:
Lass Dir mal die Pointer des Structs und von elems in C, in Python und in C beim free ausgeben.
Sirius3
User
Beiträge: 17748
Registriert: Sonntag 21. Oktober 2012, 17:20

@snow: erstens ist die c-Calling-Konvention dass der Aufrufer sich um die Parameter auf dem Stack kümmert und zweitens wird bei der üblichen 64-bit-Calling-Konvention gar keine Parameter mehr auf den Stack gelegt.
Memory-Leaks können sehr schwer zu finden sein. Ohne vollständigen Code kann man da kaum Tipps geben.
Daher sind Programmiersprachen, die sich darum kümmern auch so bequem ;-)
snow
User
Beiträge: 25
Registriert: Mittwoch 4. Juli 2012, 08:52

@jerch: In meinem C-Testprogramm für die C Lib :
Darray wurde erzeugt. Adresse: 0x865090
Darray elems wurde erzeugt. Adresse: 0x8650b0
Darray wurde erzeugt. Adresse: 0x865140
Darray elems wurde erzeugt. Adresse: 0x865160
Darray elems freed. Adresse: 0x8650b0
Darray freed. Adresse: 0x865090
Darray elems freed. Adresse: 0x865160
Darray freed. Adresse: 0x865140

Und im Python Code nach eingabe der ersten Variablen:
Darray wurde erzeugt. Adresse: 0x1a9a6d0
Darray elems wurde erzeugt. Adresse: 0x1d0e3b0
Darray wurde erzeugt. Adresse: 0x1d083e0
Darray elems wurde erzeugt. Adresse: 0x1d0e4a0
Darray elems freed. Adresse: 0x1d0e3b0
Darray freed. Adresse: 0x1a9a6d0

@Sirius3 aber immerhin gibt es in C/C++ ja Programme wie valgrind die einem dabei helfen diese Memeory-Leaks zu finden. Aber wär schon was, wenn es eine Sprache gibt die so bequem wie Python ist und gleichzeitig so performant wie C. \dreamoff ^^
Zuletzt geändert von snow am Dienstag 12. November 2013, 21:35, insgesamt 3-mal geändert.
BlackJack

@snow: Ich würde sagen mit raten kommt man hier nicht weiter. Man bräuchte ein minimales Beispiel was jeder nachvollziehen kann, also bei sich selber auf dem Rechner kompilieren und laufen lassen kann, was das Problem aufzeigt.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@snow:
Im Python Bsp fehlt ja was - wenn er da abgebrochen hast, wäre der "korrupte" Pointer ausm Stacktrace noch ganz hilfreich ;)
snow
User
Beiträge: 25
Registriert: Mittwoch 4. Juli 2012, 08:52

edit: Ok Fehler ist gelöst xD Habe gerade noch eine Bedingung in die Python Methode gesetzt, dass wenn der Text leer ist, die Methode gar nicht ausgeführt werden soll und einfach ein leeres set zurück gegeben werden soll, mit dem Ziel weniger printouts zu haben. Nachdem ich gerade einfach aus Spaß den free Aufruf nochmal in die Methode gepackt habe ist das Programm auf einmal nicht mehr abgestürzt. :P

edit2: Nun ist auch der Grund gefunden warum es bei einem leeren String abgestürzt ist.

Code: Alles auswählen

DArray *newDArray() {
    DArray *arr = (DArray*) malloc(sizeof(DArray));
    arr->elems = NULL;
    arr->length = 0;
    arr->capacity = 0;
    return arr;
}

void dArrayFree(DArray *arr) {
    if (arr != NULL) {
        if (arr->elems != NULL){
            uint i;
            for (i = 0; i < arr->length; i++) {
                free(arr->elems[i]);
            }
            free(arr->elems);
        }
        free(arr);
    }
}
Da ich vorher nicht in der Methode newDArray den Pointer auf NULL gesetzt habe, wurde natürlich die Adresse auf irgendwas gesetzt. Die Prüfung in der Free Methode konnte natürlich nicht greifen und wollte irgendwo im Nirvana Speicher frei machen. Ich weiß, böser Patzer :oops:
Trotzdem danke für eure Hilfe :D
Antworten