【问题标题】:Haskell FFI Support for Functions With Variadic ArgumentsHaskell FFI 对具有可变参数的函数的支持
【发布时间】:2011-05-13 08:49:59
【问题描述】:

谁能向我展示一个使用带有可变参数的 C 函数(例如 printf)和 Haskell 的外部函数接口的示例?我尝试搜索 HaskellWiki,但没有找到这样的示例。

谢谢!

【问题讨论】:

  • 您可以查看如何从 Haskell 调用 printf(在 Printf-THprintf-mauke 包中)。
  • @TomMD:我很确定这两个只是在 Haskell 中对 printf 概念的重新发明,而不是绑定到 C 实现。
  • @mokus:啊,谢谢。我认为它完成了幕后工作。

标签: haskell ffi variadic-functions


【解决方案1】:

我认为这是不可能的。但是,您可以对同一个 C 函数进行多个外部导入,并为其赋予不同的 Haskell 名称和 Haskell 类型。不过,我不确定它是否 100% 便携。

【讨论】:

  • 这也是我采用的方法。
  • 它没有得到 FFI 规范的官方支持,尽管我们在 GHC 中遇到了一些麻烦以确保它可以正常工作(特别是 x86_64 ABI 使得绑定到 varargs 函数有点痛苦)。
  • 来自 Haskell 2010 规范 (8.5.1):“请注意,对于定义为接受可变数量参数的 C 函数,所有超出显式类型参数的参数都会受到参数提升。但是,因为 C允许此类函数的调用约定不同,Haskell 系统通常不能使用可变参数函数。因此,在可移植代码中不推荐使用它们。”
【解决方案2】:

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

【讨论】:

【解决方案3】:

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

GHC Users' Guide - The CAPI calling convention

【讨论】:

    【解决方案4】:

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

    我承认我是 Haskell 爱好者。每当我为了娱乐而编程时,我通常更喜欢这种语言,因为它优雅。

    目前我正在处理与 GEGL 库的 Haskell 绑定。这背后的动机是我希望涉足游戏开发,并且我需要一个库来在 SDL Surfaces 上绘图。我显然不是真正的简单解决方案的粉丝,我尝试学习新事物。就像使用 Haskell FFI。

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

    为了构建我的绑定,我不使用 Haskell 的标准 FFI,而是使用 Haskell 库 inline-c 直接调用 C 函数而不使用刚性绑定。这是在 inline-c 中通过将函数调用包装到 QuasiQuoter 中来实现的。正如我之前所说,这仍然需要你为每个调用这个函数的情况编写一个 QuasiQuoter,但你不必用外部 import ccall 声明来混乱你的代码。

    为了限制您的情况,我建议使用 sum 类型作为函数参数。 sum 类型是具有多个构造函数的类型。您可以使用 Haskell 的模式匹配为需要接口和区分它们的每种情况创建一个构造函数。您可以查看有关如何制作所有这些的示例in my bindings

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-05
      • 1970-01-01
      相关资源
      最近更新 更多