Поиск…


Синтаксис

  • 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


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow