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.
精彩评论