2013-03-28 9 views
8

Dobrze (twoja lokalna pora dnia), wszyscy.Testowanie kodu FFI (z "importem zagranicznym") z GHCi

Przeszedłem przez Real World Haskell's chapter w interfejsie funkcji zagranicznej, i zrobiłem kilka następnych odczytów here. Eksperymentuję teraz z wiązaniem funkcji C i , a ja chciałbym wyjaśnienia niektórych rzeczy.

co następuje dość jasne:

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

mogę załadować to i kod, który używa go w ghci, i wszystko jest w porządku. To nawet ładuje wbudowane ghci w trybie Haskella emacsa. Uważam to za świetne do testowania. math to biblioteka systemowa, więc jest to proste.

Teraz przykład z Real World Haskell:

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

zostawiłem resztę podpisu funkcji celowo. Teraz nie mogę załadować tego w trybie Haskella . Wszystkie przykłady widziałem powiedzieć to musi być zrobione:

ghci -lpcre 

Co zrobić i uzyskać natychmiastowe potwierdzenie, że rzeczy są ładowane poprawnie:

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 

mogę następnie załadować moje wiązania kod i przetestować z dala, ale ...

_Question 1_ Czy mogę załadować biblioteki niesystemowe z ghci, jak pcre? To by pozwoliło mi przetestować w emacs.

Przeprowadzka. Rzeczy stają się bardziej skomplikowane, gdy próbuję napisać powiązania z moim kodem z własnym kodem.

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

Co prawda dość bezcelowa funkcja. Zajmuje ByteString z Haskell i Drukuje C. Oto prosty plik testowy:

{-# 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 

udało mi się skompilować ten wykonując:

ghc --make myprint.c printTest.hs 

i uzyskać plik wykonywalny, ale Nie byłem w stanie załadować go w ogóle w ghci. To znacznie opóźnia proces testowania.

_Question 2_ Co muszę zrobić, aby załadować kod Haskell w ghci który wiąże się mojego kodu C? Żadne z głównych źródeł informacji FFI nie miało nic do powiedzenia na ten temat. Żadna liczba manipulacji przy użyciu ghci -L nie sprawi, że zadziała.

Serdecznie dziękuję za wszelką pomoc, jaką możesz zaoferować.

+2

Q2, musisz skompilować myprint.c jako bibliotekę, jeśli chcesz ją załadować w ghci. To zależy od kompilatora/os. – Jonke

+1

Wzdłuż tych linii (gcc -c -fPIC foo.c -o foo.o, a następnie gcc -shared -Wl, -soname, libfoo.so.1 -o libfoo.so.1.0.1 foo.o) lub coś w stylu ten – Jonke

Odpowiedz

9

ghci załaduje dowolną bibliotekę, o ile jest ona ważna dla architektury i może znajdować się na jakiejś ścieżce.W systemie Windows nazwy ścieżek ze spacjami powodują problemy, ale nie wiem, czy nadal to robią.

Aby załadować swój własny kod w ghci, najpierw trzeba go skompilować, a następnie powiedzieć ghci załadować wyjście że:

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> 

Można również przygotować pliki C do biblioteki i załadować to do ghci, ale dla jednego pliku przy użyciu pliku obiektowego jest całkiem wygodne. Jeśli chcesz utworzyć bibliotekę, zalecane polecenie, takie jak @Jonke powinno działać. W moim systemie (OSX),

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

na mój system działa również po prostu użyć filepath biblioteki jako argument, ale nie wiem, czy to przenośny.

+0

'ghci myprint.o' Wydaje się to być najprostszym sposobem. Dziękuję Ci. –

+0

Jak postępować, jeśli myprint.o zależy od statycznej biblioteki C zawartej we względnym katalogu (np .: lib/libx.a) –

+1

@ThiadodeArruda, musisz dodać niezbędne znaczniki linkera, tak jakbyś kompilował. Coś w rodzaju '-Llib -lx -lmyprint' powinno działać. Flagi linków mogą być wrażliwe na zamawianie, więc być może trzeba trochę popracować, aby znaleźć prawidłowe uporządkowanie, jeśli masz dużo bibliotek do połączenia. –