Haskell Language
Interfaz de función extranjera
Buscar..
Sintaxis
- importación extranjera ccall inseguro "foo" hFoo :: Int32 -> IO Int32 {- Importa una función llamada
foo
en algún archivo de objeto, y define el símbolohFoo
que se puede llamar con el código de Haskell. -}
Observaciones
Si bien cabal tiene soporte para incluir bibliotecas de C y C ++ en un paquete de Haskell, hay algunos errores. Primero, si tiene datos (en lugar de una función) definidos en bo
que se usan en ao
, y liste las C-sources: ac, bc
, entonces Cabal no podrá encontrar los datos. Esto está documentado en # 12152 . Una solución alternativa al utilizar cabal es reordenar la lista de C-sources
C-sources: bc, ac
para que sean C-sources: bc, ac
. Es posible que esto no funcione cuando se usa la pila, porque la pila siempre vincula las C-sources
alfabéticamente, independientemente del orden en el que se enumeran.
Otro problema es que debe rodear cualquier código C ++ en los archivos de encabezado (.h) con guardias #ifdef __cplusplus
. Esto se debe a que GHC no entiende el código C ++ en los archivos de encabezado. Aún puede escribir código C ++ en archivos de encabezado, pero debe rodearlo con guardas.
ccall
refiere a la convención de llamada ; actualmente se ccall
y stdcall
(convención de Pascal). La palabra clave unsafe
es opcional; esto reduce la sobrecarga para funciones simples, pero puede causar interbloqueos si la función foránea se bloquea indefinidamente o no tiene suficiente permiso para ejecutar 1 .
Llamando C desde Haskell
Por razones de rendimiento, o debido a la existencia de bibliotecas C maduras, es posible que desee llamar al código C desde un programa de Haskell. Este es un ejemplo simple de cómo puede pasar datos a una biblioteca de C y obtener una respuesta.
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
La palabra clave unsafe
genera una llamada más eficiente que "segura", pero requiere que el código C nunca realice una devolución de llamada al sistema Haskell. Ya que foo
está completamente en C y nunca llamará a Haskell, podemos usar unsafe
.
También necesitamos dar instrucciones a Cabal para compilar y enlazar en C fuente.
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
Entonces puedes correr:
> cabal configure
> cabal build foo
> ./dist/build/foo/foo
42
Pasar funciones de Haskell como devoluciones de llamada a código C.
Es muy común que las funciones C acepten punteros a otras funciones como argumentos. El ejemplo más popular es configurar una acción para que se ejecute cuando se hace clic en un botón en alguna biblioteca de herramientas de GUI. Es posible pasar las funciones de Haskell como C callbacks.
Para llamar a esta función C:
void event_callback_add (Object *obj, Object_Event_Cb func, const void *data)
Primero lo importamos al código de Haskell:
foreign import ccall "header.h event_callback_add"
callbackAdd :: Ptr () -> FunPtr Callback -> Ptr () -> IO ()
Ahora, observando cómo se define Object_Event_Cb
en el encabezado C, defina qué Callback
está en Haskell:
type Callback = Ptr () -> Ptr () -> IO ()
Finalmente, cree una función especial que envuelva la función Haskell de tipo Callback
en un puntero FunPtr Callback
:
foreign import ccall "wrapper"
mkCallback :: Callback -> IO (FunPtr Callback)
Ahora podemos registrar el callback con código C:
cbPtr <- mkCallback $ \objPtr dataPtr -> do
-- callback code
return ()
callbackAdd cpPtr
Es importante liberar FunPtr
asignado una vez que FunPtr
registro de la devolución de llamada:
freeHaskellFunPtr cbPtr