Haskell Language
Интерфейс внешней функции
Поиск…
Синтаксис
- foreign import ccall unsafe «foo» hFoo :: Int32 -> IO Int32 {- импортирует функцию с именем
foo
в некоторый объектный файл и определяет символhFoo
который можно вызвать с кодом Haskell. -}
замечания
Хотя у cabal есть поддержка для включения библиотек C и C ++ в пакет Haskell, есть несколько ошибок. Во-первых, если у вас есть данные (а не функция), определенные в bo
которые используются в ao
, и перечислите C-sources: ac, bc
, то cabal не сможет найти данные. Это подтверждено в # 12152 . Обходной путь при использовании cabal состоит в том, чтобы переупорядочить список C-sources
как C-sources: bc, ac
. Это может не работать при использовании стека, потому что стек всегда связывает C-sources
алфавитном порядке, независимо от того, в каком порядке вы их перечисляете.
Еще одна проблема заключается в том, что вы должны окружать любой код C ++ в файлах заголовка (.h) с помощью #ifdef __cplusplus
. Это связано с тем, что GHC не понимает код C ++ в файлах заголовков. Вы все еще можете писать код C ++ в заголовочных файлах, но вы должны окружить его защитой.
ccall
относится к вызывающему соглашению ; в настоящее время ccall
и stdcall
(соглашение Pascal). unsafe
ключевое слово необязательно; это уменьшает накладные расходы для простых функций, но может вызвать взаимоблокировки, если посторонняя функция блокирует бесконечно или имеет недостаточное разрешение для выполнения 1 .
Вызов C из Haskell
По соображениям производительности или из-за наличия зрелых библиотек C вы можете вызвать код C из программы Haskell. Вот простой пример того, как вы можете передавать данные в библиотеку C и получать ответ.
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
ключевое слово генерирует более эффективный вызов, чем «безопасный», но требует, чтобы код C никогда не вызывал обратную связь с системой Haskell. Так как foo
полностью в C и никогда не вызовет Haskell, мы можем использовать unsafe
.
Нам также необходимо дать инструкции cabal для компиляции и ссылки в источнике 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
Затем вы можете запустить:
> cabal configure
> cabal build foo
> ./dist/build/foo/foo
42
Передача Haskell выполняет функции обратного вызова кода C.
Для функций C очень часто принимают указатели на другие функции в качестве аргументов. Наиболее популярным примером является установка действия, которое будет выполняться при нажатии кнопки в некоторой библиотеке инструментов GUI. Функции Haskell можно передавать как C-обратные вызовы.
Чтобы вызвать эту функцию C:
void event_callback_add (Object *obj, Object_Event_Cb func, const void *data)
мы сначала импортируем его в код Haskell:
foreign import ccall "header.h event_callback_add"
callbackAdd :: Ptr () -> FunPtr Callback -> Ptr () -> IO ()
Теперь, посмотрев, как Object_Event_Cb
определен в заголовке C, определите, что Callback
находится в Haskell:
type Callback = Ptr () -> Ptr () -> IO ()
Наконец, создайте специальную функцию, которая будет обертывать функцию Haskell типа Callback
в указатель FunPtr Callback
:
foreign import ccall "wrapper"
mkCallback :: Callback -> IO (FunPtr Callback)
Теперь мы можем зарегистрировать обратный вызов с кодом C:
cbPtr <- mkCallback $ \objPtr dataPtr -> do
-- callback code
return ()
callbackAdd cpPtr
Важно освободить выделенный FunPtr
только вы FunPtr
обратный вызов:
freeHaskellFunPtr cbPtr