5 Stimmen

C2HS mit "fremden" Zeigern kann nicht funktionieren

Allgemeine Informationen

Ich experimentiere derzeit mit dem C->Haskell (C2HS) Interface Generator für Haskell. Auf den ersten Blick war es einfach großartig, ich habe eine ziemlich komplizierte C++-Bibliothek (mit einer kleinen extern C -Wrapper) in nur ein paar Stunden. (Und ich habe noch nie ein FFI gemacht.)

Es gab nur ein Problem: Wie bekommt man den in der C/C++-Bibliothek zugewiesenen Speicher wieder frei? Ich fand {#pointer ... foreign #} im C2HS-Dokumentation und das sieht genau nach dem aus, was ich suche. Da mein C-Wrapper die C++ Bibliothek in eine Bibliothek mit referenzieller Transparenz mit einer funktionalen Schnittstelle verwandelt, sollte der Haskell Storage Manager in der Lage sein, die harte Arbeit für mich zu erledigen :-). Leider ist es mir nicht gelungen, dies zum Laufen zu bringen. Um mein Problem besser zu erklären, habe ich ein kleines Demo-Projekt auf GitHub die die gleichen Eigenschaften wie die C/C++-Bibliothek+Wrapper hat, aber nicht den Overhead. Wie Sie sehen können, ist die Bibliothek völlig sicher in der Verwendung mit pure unsafe FFI.

Demo-Projekt

Auf GitHub habe ich eine kleines Demo-Projekt wie folgt organisiert:

C-Bibliothek

Die C-Bibliothek ist sehr einfach und nutzlos: Man kann ihr eine Ganzzahl übergeben und erhält so viele Ganzzahlen (derzeit [0..n] ) aus der Bibliothek zurück. Denken Sie daran: Die Bibliothek ist nutzlos, nur eine Demo. Auch die Schnittstelle ist recht einfach: Die Funktion LTIData lti_new_data(int n) gibt (nach Übergabe einer ganzen Zahl) eine Art undurchsichtiges Objekt zurück, das die von der C-Bibliothek zugewiesenen Daten enthält. Die Bibliothek hat auch zwei Accessor-Funktionen int lti_element_count(LTIData data) y int lti_get_element(LTIData data, int n) gibt ersteres die Anzahl der Elemente und letzteres Ihr Element zurück n . Ah, und zu guter Letzt sollte der Benutzer der Bibliothek nach der Benutzung die undurchsichtige LTIData mit void lti_free_data(LTIData data) .

Haskell-Bindung auf niedriger Ebene

Die Low-Level-Haskell-Bindung wird mit C2HS eingerichtet, Sie finden sie in

Haskell-API auf hoher Ebene

Zum Spaß habe ich auch eine Art Haskell-API auf hoher Ebene unter Verwendung der Low-Level-API-Bindung und einer einfaches Treiberprogramm die die hochrangige API verwendet. Mit dem Treiberprogramm und z.B. valgrind kann man leicht den ausgelaufenen Speicher sehen (für jeden Parameter p_1, p_2, ..., p_n die Bibliothek tut \sum_{i = 1..n} 1 + p_i Zuweisungen; leicht zu beobachten wie unten):

$ valgrind dist/build/TestHsLTI/TestHsLTI 100             2>&1 | grep -e allocs -e frees
==22647==   total heap usage: 184 allocs, 74 frees, 148,119 bytes allocated

$ valgrind dist/build/TestHsLTI/TestHsLTI 100 100         2>&1 | grep -e allocs -e frees
==22651==   total heap usage: 292 allocs, 80 frees, 181,799 bytes allocated

$ valgrind dist/build/TestHsLTI/TestHsLTI 100 100 100     2>&1 | grep -e allocs -e frees
==22655==   total heap usage: 400 allocs, 86 frees, 215,479 bytes allocated

$ valgrind dist/build/TestHsLTI/TestHsLTI 100 100 100 100 2>&1 | grep -e allocs -e frees
==22659==   total heap usage: 508 allocs, 92 frees, 249,159 bytes allocated

Aktueller Stand der Demo

Sie sollten in der Lage sein, das Programm zu klonen, zu kompilieren und auszuführen. Projekt durch einfache Eingabe von git clone https://github.com/weissi/c2hs-experiments.git && cd c2hs-experiments && cabal configure && cabal build && dist/build/TestHsLTI/TestHsLTI

Was ist also das Problem?

Das Problem ist, dass das Projekt nur Foreign.Ptr und nicht die "verwaltete" Version Foreign.ForeignPtr unter Verwendung der C2HS {#pointer ... foreign #} und ich kann es nicht zum Laufen bringen. Im Demoprojekt habe ich auch eine .chs Datei, die versucht, diese fremden Zeiger zu verwenden aber es funktioniert nicht :-(. Ich habe es sehr hart versucht, aber ich hatte keinen Erfolg.

Und eine Sache verstehe ich auch nicht: Wie kann ich GHC mit C2HS sagen, wie man die Daten der Bibliothek freigibt. Die Bibliothek des Demoprojekts bietet eine Funktion void lti_free_data(LTIData data) die aufgerufen werden sollte, um den Speicher freizugeben. Aber GHC kann das nicht erraten!?! Wenn GHC regelmäßig ein free() wird nicht der gesamte Speicher freigegeben :-(.

3voto

Johannes Weiss Punkte 49967

Problem gelöst: Ich fand diese Datei macht etwas Ähnliches im Internet und konnte das Problem lösen :-).

Alles, was es brauchte, war ein einfacher Rangiercode:

foreign import ccall "lib_to_interface.h &lti_free_data"
  ltiFreeDataPtr :: FunPtr (Ptr (LTIDataHs) -> IO ())

newObjectHandle :: Ptr LTIDataHs -> IO LTIDataHs
newObjectHandle p = do
  fp <- newForeignPtr ltiFreeDataPtr p
  return $ LTIDataHs fp

Hier ist die endgültig verwaltet ( ForeignPtr ) Version des .chs Datei .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X