Поддержка Haskell FFI для функций с различными аргументами - PullRequest
11 голосов
/ 13 мая 2011

Может кто-нибудь показать мне пример использования функции C с переменными аргументами (например, printf) с интерфейсом внешних функций Haskell?Я попытался поискать в HaskellWiki, но не нашел таких примеров.

Спасибо!

Ответы [ 4 ]

7 голосов
/ 13 мая 2011

Я не думаю, что это возможно.Однако вы можете сделать несколько внешних импортов одной и той же функции C и присвоить ей разные имена Haskell и типы Haskell.Я не уверен, что это на 100% портативно.

5 голосов
/ 13 мая 2011

Вы можете использовать интерфейс Haskell для libffi (http://hackage.haskell.org/package/libffi),, как в этом коде дословно скопировано из проекта, над которым я работаю (вы можете увидеть его в контексте на https://github.com/mokus0/bindings-hdf5/blob/master/src/Bindings/HDF5/Raw/H5E.hsc). Эта конкретная функция также проверяетв случае отсутствия аргументов и при возможности напрямую вызывает функцию C, чтобы избежать небольших накладных расходов, связанных с libffi.

-- libffi to the rescue!  I have no idea how I'd wrap this without it, and there
-- doesn't appear to be a non-deprecated non-private non-varargs equivalent.
-- 
-- |Pushes a new error record onto error stack for the current
-- thread.  The error has major and minor IDs 'maj_id' and
-- 'min_id', the name of a function where the error was detected,
-- the name of the file where the error was detected, the
-- line within that file, and an error description string.  The
-- function name, file name, and error description strings must
-- be statically allocated.
-- 
-- Returns non-negative on success/Negative on failure.
-- 
-- > herr_t H5Epush2(hid_t err_stack, const char *file, const char *func, unsigned line,
-- >     hid_t cls_id, hid_t maj_id, hid_t min_id, const char *msg, ...);
--
-- (msg is a printf format string, the varargs are the format parameters)
h5e_push2 :: HId_t -> CString -> CString -> CUInt -> HId_t -> HId_t -> HId_t -> CString -> [Arg] -> IO HErr_t
h5e_push2 err_stack file func line cls_id maj_id min_id fmt [] =
    h5e_push2_no_varargs err_stack file func line cls_id maj_id min_id fmt
h5e_push2 (HId_t err_stack) file func line (HId_t cls_id) (HId_t maj_id) (HId_t min_id) fmt varargs =
    callFFI p_H5Epush2 retHErr_t args
    where 
        argHId_t = arg#type hid_t
        retHErr_t = fmap HErr_t (ret#type herr_t)

        args = argHId_t err_stack : argPtr file : argPtr func : argCUInt line
             : argHId_t cls_id : argHId_t maj_id : argHId_t min_id : argPtr fmt
             : varargs

foreign import ccall "H5Epush2"
    h5e_push2_no_varargs :: HId_t -> CString -> CString -> CUInt -> HId_t -> HId_t -> HId_t -> CString -> IO HErr_t
foreign import ccall "&H5Epush2"
    p_H5Epush2 :: FunPtr (HId_t -> CString -> CString -> CUInt -> HId_t -> HId_t -> HId_t -> CString -> IO HErr_t)
1 голос
/ 09 сентября 2016

В последних версиях GHC вы можете использовать расширение CApiFFI для импорта функций с переменным аргументом C.

Руководство пользователя GHC - Соглашение о вызовах CAPI

0 голосов
/ 27 июня 2019

https://nek0.eu/posts/2016-04-19-Interfacing-variadic-functions-from-Haskell.html

Признаюсь, я поклонник Хаскелла. Всякий раз, когда я программирую что-то для удовольствия, я обычно предпочитаю этот язык из-за его элегантности.

В настоящее время я работаю над привязками Haskell к библиотеке GEGL. Мотивация этого - мое желание заниматься разработкой игр, и мне нужна библиотека для рисования на поверхностях SDL. Я, очевидно, не очень люблю простые решения и стараюсь учиться чему-то новому. Как и использование Haskell FFI.

Во время написания привязок я столкнулся с проблемой, что GEGL предоставляет в своем заголовке переменные функции, которые мне нужно связать. Это создает серьезную проблему для Haskell, потому что число аргументов функции должно быть постоянным. Просто невозможно определить функцию, не зная, сколько у нее аргументов и какого типа у каждого аргумента. Это остается верным даже для моего решения. Единственная причина, по которой мое решение работает, заключается в том, что я могу ограничить случаи, когда интерфейс этих функций варьируется до приемлемого уровня.

Для построения моих привязок я не использую стандартную FFI Haskell, а библиотеку Haskell inline-c для непосредственного вызова функций C без использования жестких привязок. Это достигается в inline-c путем помещения вызова функции в QuasiQuoter. Как я уже говорил ранее, для этого по-прежнему требуется писать QuasiQuoter для каждого случая, когда вызывается эта функция, но вам не нужно загромождать свой код объявлениями зарубежного импорта ccall.

Для ограничения ваших дел я рекомендую использовать тип суммы в качестве аргумента функции. Тип суммы - это тип, который имеет несколько конструкторов. У вас может быть конструктор для каждого случая, который вам необходим для взаимодействия и различения между ними с помощью сопоставления с шаблоном Haskell. Вы можете увидеть пример того, как сделать все это в моих привязках .

...