开发者

Haskell FFI Support for Functions With Variadic Arguments

Can anyone show me an example of using a C function with variadic arguments (e.g. printf) with Haskell'开发者_StackOverflow中文版s Foreign Function Interface? I tried searching the HaskellWiki, but found no such examples.

Thanks!


I don't think that's possible. You can, however, make several foreign imports of the same C function and give it different Haskell names and Haskell types. I'm not sure that is 100% portable, though.


You can use the Haskell interface to libffi (http://hackage.haskell.org/package/libffi), as in this code copied verbatim out of a project I'm working on (you can see it in context at https://github.com/mokus0/bindings-hdf5/blob/master/src/Bindings/HDF5/Raw/H5E.hsc). This particular function also checks for the no-arguments case and calls the C function directly when possible to avoid the small overhead associated with 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)


In recent versions of GHC you can use the CApiFFI extension to import variable-argument C functions.

GHC Users' Guide - The CAPI calling convention


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

I confess I am a Haskell aficionado. Whenever I program something for pleasure, I usually prefer this language because of its elegance.

Currently I am working on Haskell bindings to the GEGL library. The motivation behind this my desire to dabble in Game development and I have the need for a library to draw on SDL Surfaces. I am obviously not really a fan of the easy solutions and I try to learn new things. Like using the Haskell FFI.

While writing the bindings I encountered the problem, that GEGL exposes variadic functions in its header which I need to interface. This poses a serious Problem for Haskell because the number of function arguments has to be constant. There is simply no way defining a function without knowing how many arguments it has and of what type each argument is. This stays true even for my solution. The only reason my solution works is that I can limit the cases how to interface these variadic functions to a manageable amount.

To build my bindings I do not use the standard FFI of Haskell, but the Haskell library inline-c to call the C functions directly without using rigid bindings. This is achieved in inline-c by wrapping the function call into a QuasiQuoter. As I said earlier, this still requires you to write a QuasiQuoter for every case this function gets called, but you don’t have to clutter your code with foreign import ccall declarations.

For limiting your cases I recommend using a sum type as a function argument. A sum type is a type which has multiple constructors. You can have a constructor for each case you need to interface and distinguish between them using Haskell’s pattern matching. You can see an example on how to make all this in my bindings.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜