Haskell Language
Interfejs funkcji zagranicznej
Szukaj…
Składnia
- import zagraniczny ccall niebezpieczne „foo” hFoo :: Int32 -> IO Int32 {- Importuje funkcję o nazwie
foo
do jakiegoś pliku obiektowego i definiuje symbolhFoo
który można wywołać za pomocą kodu Haskell. -}
Uwagi
Chociaż Cabal obsługuje obsługę bibliotek C i C ++ w pakiecie Haskell, istnieje kilka błędów. Po pierwsze, jeśli masz dane (a nie funkcję) zdefiniowane w bo
które są używane w ao
, i wymień C-sources: ac, bc
, wtedy cabal nie będzie w stanie znaleźć danych. Jest to udokumentowane w # 12152 . Obejściem tego problemu jest zmiana kolejności listy C-sources
C-sources: bc, ac
. Może to nie działać podczas używania stosu, ponieważ stos zawsze łączy C-sources
alfabetycznie, bez względu na kolejność ich wyświetlania.
Innym problemem jest to, że musisz otoczyć dowolny kod C ++ w plikach nagłówka (.h) za pomocą #ifdef __cplusplus
. Wynika to z faktu, że GHC nie rozumie kodu C ++ w plikach nagłówkowych. Nadal możesz pisać kod C ++ w plikach nagłówków, ale musisz go otoczyć strażnikami.
ccall
odnosi się do konwencji wywoływania ; obecnie ccall
są ccall
i stdcall
(konwencja Pascala). unsafe
słowo kluczowe jest opcjonalne; zmniejsza to narzut związany z prostymi funkcjami, ale może powodować zakleszczenia, jeśli obce bloki funkcyjne będą działać w nieskończoność lub nie będą miały wystarczających uprawnień do wykonania 1 .
Dzwonię do C z Haskell
Ze względu na wydajność lub z powodu istnienia dojrzałych bibliotek C możesz wywołać kod C z programu Haskell. Oto prosty przykład, w jaki sposób możesz przekazać dane do biblioteki C i uzyskać odpowiedź z powrotem.
foo.c:
#include <inttypes.h>
int32_t foo(int32_t a) {
return a+1;
}
Foo.hs:
import Data.Int
main :: IO ()
main = print =<< hFoo 41
foreign import ccall unsafe "foo" hFoo :: Int32 -> IO Int32
unsafe
słowo kluczowe generuje bardziej wydajne wywołanie niż „bezpieczne”, ale wymaga, aby kod C nigdy nie oddzwaniał do systemu Haskell. Ponieważ foo
jest całkowicie w C i nigdy nie zadzwoni do Haskell, możemy użyć unsafe
.
Musimy także poinstruować cabal, aby kompilował i łączył w źródle C.
foo.cabal:
name: foo
version: 0.0.0.1
build-type: Simple
extra-source-files: *.c
cabal-version: >= 1.10
executable foo
default-language: Haskell2010
main-is: Foo.hs
C-sources: foo.c
build-depends: base
Następnie możesz uruchomić:
> cabal configure
> cabal build foo
> ./dist/build/foo/foo
42
Przekazywanie funkcji Haskell jako wywołań zwrotnych do kodu C.
Funkcje C bardzo często przyjmują wskaźniki jako argumenty dla innych funkcji. Najbardziej popularnym przykładem jest ustawienie akcji, która ma być wykonywana po kliknięciu przycisku w bibliotece narzędzi GUI. Możliwe jest przekazywanie funkcji Haskell jako wywołań zwrotnych C.
Aby wywołać tę funkcję C:
void event_callback_add (Object *obj, Object_Event_Cb func, const void *data)
najpierw importujemy go do kodu Haskell:
foreign import ccall "header.h event_callback_add"
callbackAdd :: Ptr () -> FunPtr Callback -> Ptr () -> IO ()
Teraz patrząc na to, jak zdefiniowano Object_Event_Cb
w nagłówku C, zdefiniuj, co jest Callback
Object_Event_Cb
w Haskell:
type Callback = Ptr () -> Ptr () -> IO ()
Na koniec utwórz specjalną funkcję, która FunPtr Callback
funkcję Haskell typu Callback
we wskaźnik FunPtr Callback
:
foreign import ccall "wrapper"
mkCallback :: Callback -> IO (FunPtr Callback)
Teraz możemy zarejestrować połączenie zwrotne za pomocą kodu C:
cbPtr <- mkCallback $ \objPtr dataPtr -> do
-- callback code
return ()
callbackAdd cpPtr
Ważne jest, aby zwolnić przydzielony FunPtr
po wyrejestrowaniu oddzwaniania:
freeHaskellFunPtr cbPtr