2013-03-28 2 views
8

Gut (Ihre lokale Tageszeit), alle.Testen von FFI-Code (mit "fremden Import" s) mit GHCi

Ich ging durch Real World Haskell's chapter auf der Fremdfunktionsschnittstelle, und tat einige Follow-up-Lesung here. Ich experimentiere jetzt mit dem Binden von an C-Funktionen, und ich hätte gerne einige Erläuterungen zu einigen Dingen.

Nachstehend ist ziemlich klar:

foreign import ccall unsafe "math.h sin" c_sin :: CDouble -> CDouble 

ich dies und Code laden, die es in GHCI verwendet, und alles ist in Ordnung. Es sogar lädt in der eingebetteten Ghci in Emacs Haskell-Modus. Ich finde das großartig zum Testen. math ist eine Systembibliothek, so dass dies einfach ist.

nun ein Beispiel von Real World Haskell:

foreign import ccall unsafe "pcre.h pcre_compile" c_pcre_compile :: ... 

Ich habe absichtlich den Rest der Funktionssignatur weggelassen. Jetzt kann ich das nicht in Haskell-Modus laden. Alle Beispiele, die ich gesehen habe, sagen, dass dies getan werden muss:

ghci -lpcre 

Was ich tun, und sofortige Bestätigung, dass die Dinge nicht ordnungsgemäß geladen:

GHCi, version 7.6.2: http://www.haskell.org/ghc/ :? for help 
Loading package ghc-prim ... linking ... done. 
Loading package integer-gmp ... linking ... done. 
Loading package base ... linking ... done. 
Loading object (dynamic) /usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../lib/libpcre.so ... done 
final link ... done 

dann kann ich laden meine Bindung Code und Test weg, aber ...

_Question 1_ Kann ich non-system Bibliotheken von innerhalb ghci laden, wie pcre? Dies würde erlauben mir innerhalb von Emacs zu testen.

Weitergehen. Dinge werden komplizierter, wenn ich versuche, Bindungen zu meinem eigenen C-Code zu schreiben.

foreign import ccall unsafe "myprint.h myprint" c_myprint :: CString -> IO() 

Zugegebenermaßen eine sinnlose Funktion. Es dauert eine ByteString von Haskell und druckt es mit C. Hier ist eine einfache Testdatei ist:

{-# LANGUAGE ForeignFunctionInterface #-} 
-- printTest.hs 

import Foreign 
import Foreign.C.Types 
import Foreign.C.String 

import qualified Data.ByteString.Char8 as B 

--- 

foreign import ccall unsafe "myprint.h myprint" c_myprint :: CString -> IO() 

--- 

main = B.useAsCString (B.pack "Tempura is great!") c_myprint 

ich diese Lage gewesen, zu kompilieren, indem Sie:

ghc --make myprint.c printTest.hs 

Und eine ausführbare Datei, aber Ich konnte es überhaupt nicht in Ghci laden. Dies ist dem Testprozess stark abträglich.

_Question 2_ Was muss ich tun Haskell Code in GHCI zu laden, die zu mein C-Code bindet? Keine der wichtigsten Quellen von FFI-Informationen hatte dazu etwas zu sagen. Keine Menge des Fiedelns mit ghci -L könnte es zur Arbeit bringen.

Bitte und danke für jede Hilfe, die Sie anbieten können.

+2

Q2, Sie müssen myprint.c als Bibliothek kompilieren, wenn Sie es in Ghci laden möchten. Dies ist c Compiler/os abhängig. – Jonke

+1

Entlang dieser Zeilen (gcc -c-fPIC foo.c -o foo.o und dann gcc -shared -Wl, -Soname, libfoo.so.1 -o libfoo.so.1.0.1 foo.o) oder so ähnlich dass – Jonke

Antwort

9

ghci lädt eine beliebige Bibliothek, solange sie für Ihre Architektur gültig ist und sich auf einem Pfad befinden kann.Auf Windows, Pfadnamen mit Leerzeichen, die Probleme verursachen, weiß ich nicht, ob sie immer noch tun.

Um Ihren eigenen Code in GHCI zu laden, müssen Sie es zunächst kompilieren, dann GHCI sagen die Ausgabe, dass zu laden:

mybox$ gcc -c myprint.c 
mybox$ ghci Myprint.hs myprint.o 

*Main> main 
Loading package array-0.4.0.1 ... linking ... done. 
Loading package deepseq-1.3.0.1 ... linking ... done. 
Loading package bytestring-0.10.0.2 ... linking ... done. 
Tempura is great! 
*Main> 

Sie auch die C-Dateien in einer Bibliothek zusammenstellen konnte und laden dass in ghci, aber für nur eine Datei mit einer Objektdatei ist sehr praktisch. Wenn Sie eine Bibliothek erstellen möchten, sollte ein Befehl wie @ Jonke vorgeschlagen funktionieren. Auf meinem System (OSX),

mybox$ gcc -shared -fPIC myprint.c -o libmyprint.dylib 
mybox$ ghci -L. -lmyprint Foo.hs 

Auf meinem System funktioniert es auch nur die Bibliothek Filepath als Argument zu verwenden, aber ich weiß nicht, ob das tragbare ist.

+0

'ghci myprint.o' Dies scheint der einfachste Weg zu sein. Vielen Dank. –

+0

Wie kann ich fortfahren, wenn myprint.o von einer statischen C-Bibliothek in einem relativen Verzeichnis abhängt (zB: lib/libx.a) –

+1

@ThiadodeArruda müssen Sie die notwendigen Linker-Flags hinzufügen, als ob Sie kompilieren würden. So etwas wie '-Llib -lx -lmyprint' sollte funktionieren. Linker-Flags können jedoch für die Reihenfolge empfindlich sein. Daher müssen Sie möglicherweise etwas arbeiten, um die richtige Reihenfolge zu finden, wenn Sie viele Bibliotheken verknüpfen müssen. –