Haskell FFI支持具有可变参数的函数

时间:2011-05-13 08:49:59

标签: haskell ffi variadic-functions

有人能告诉我一个使用带有可变参数的C函数(例如printf)和Haskell的外部函数接口的例子吗?我试着搜索HaskellWiki,但没有找到这样的例子。

谢谢!

4 个答案:

答案 0 :(得分:7)

我认为这不可行。但是,您可以使用相同的C函数进行多次外部导入,并为其提供不同的Haskell名称和Haskell类型。不过,我不确定它是100%便携式的。

答案 1 :(得分:5)

您可以将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)

答案 2 :(得分:1)

在最新版本的GHC中,您可以使用CApiFFI扩展名来导入变量参数C函数。

GHC Users' Guide - The CAPI calling convention

答案 3 :(得分:0)

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

我承认我是Haskell迷。每当我为娱乐而编程时,我通常都会喜欢这种语言,因为它优雅。

目前,我正在研究Haskell与GEGL库的绑定。出于这种动机,我渴望涉足游戏开发,并且我需要一个库来利用SDL Surfaces。我显然不是很喜欢简单的解决方案,而是尝试学习新事物。就像使用Haskell FFI。

在编写绑定时,我遇到了问题,即GEGL在我需要接口的标头中公开了可变函数。这对Haskell造成了严重的问题,因为函数参数的数量必须恒定。在不知道函数有多少个参数以及每个参数的类型的情况下,根本无法定义函数。即使对于我的解决方案,也是如此。我的解决方案有效的唯一原因是,我可以将如何将这些可变参数的功能限制在可管理的范围内。

要构建绑定,我不使用Haskell的标准FFI,而是使用Haskell库inline-c直接调用C函数,而无需使用刚性绑定。通过将函数调用包装到QuasiQuoter中,可以在inline-c中实现。就像我之前说的,这仍然需要您为调用此函数的每种情况编写一个QuasiQuoter,但不必用外部导入ccall声明使代码混乱。

为限制情况,我建议使用sum类型作为函数参数。 sum类型是具有多个构造函数的类型。您可以针对需要连接的每种情况都有一个构造函数,并使用Haskell的模式匹配对其进行区分。您可以看到有关如何制作所有in my bindings的示例。