Personal tools

FFI Introduction

From HaskellWiki

(Difference between revisions)
Jump to: navigation, search
m
(Replaced GreenCard by c2hs)
 
(6 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
[[Category:Tutorials]]
 
[[Category:Tutorials]]
  +
[[Category:FFI]]
   
Haskell's FFI is used to call functions from other languages (basically C at this point), and for C to call haskell functions.
+
Haskell's FFI is used to call functions from other languages (basically C at this point), and for C to call Haskell functions.
   
 
== Links ==
 
== Links ==
   
* [[Definition#Addenda to the report]] has the official description of the FFI.
+
* The [http://www.haskell.org/onlinereport/haskell2010/haskellch8.html#x15-1490008 Foreign Function Interface section] of the Haskell 2010 report.
 
* [[FFI cook book]] has useful examples.
 
* [[FFI cook book]] has useful examples.
* [http://www.haskell.org/greencard/ http://www.haskell.org/greencard/] the greencard FFI preprocessor generates FFI code for you. the links section lists a lot of alternatives.
+
* [[FFI]]
   
 
== Short version ==
 
== Short version ==
   
There are many more useful examples in the [[FFICookBook]], but here's a few basic ones:
+
There are many more useful examples in the [[FFI cook book]], but here's a few basic ones:
   
 
<haskell>
 
<haskell>
Line 20: Line 21:
   
 
-- pure function
 
-- pure function
-- "unsafe" means it's slightly faster but can't callback to haskell
+
foreign import ccall "sin" c_sin :: CDouble -> CDouble
foreign import ccall unsafe "sin" c_sin :: CDouble -> CDouble
 
 
sin :: Double -> Double
 
sin :: Double -> Double
 
sin d = realToFrac (c_sin (realToFrac d))
 
sin d = realToFrac (c_sin (realToFrac d))
Line 28: Line 29:
   
 
<haskell>
 
<haskell>
foreign import ccall unsafe "math.h sin" c_sin :: CDouble -> CDouble
+
foreign import ccall "math.h sin" c_sin :: CDouble -> CDouble
 
</haskell>
 
</haskell>
   
However, the GHC docs say the pragma is "the Right Way" [ ''any technical reasons for this?'' ], and in practice most foreign imports will come from a small set of headers and it's easier to write them once at the top of the file.
+
[http://www.haskell.org/ghc/docs/6.10.1/html/users_guide/ffi-ghc.html#glasgow-foreign-headers GHC since 6.10.x ignores] both the <tt>INCLUDE</tt> pragma (equivalently command line <tt>-#include</tt>) and the header in the double quotes. [http://www.haskell.org/ghc/docs/6.8.3/html/users_guide/ffi-ghc.html#glasgow-foreign-headers GHC 6.8.x and before prefers] the <tt>INCLUDE</tt> pragma (equivalently command line <tt>-#include</tt>) and in Cabal package descriptions. Other compilers probably prefer the header in the double quotes (if they compile via C) or ignore (if they do not compile via C)—check their documentations.
   
Notice that C types are not the same as haskell types, and you have to import them from Foreign.C. Notice also that, as usual in haskell, you have to explicitly convert to and from haskell types. Using c_<name_of_c_function> for the raw C function is just my convention.
+
Notice that C types are not the same as Haskell types, and you have to import them from Foreign.C. Notice also that, as usual in Haskell, you have to explicitly convert to and from Haskell types. Using c_<name_of_c_function> for the raw C function is just my convention.
   
The haskell report only guarantees that Int has 30 bits of signed precision, so converting CInt to Int is not safe! On the other hand, many classes have instances for Int and Integer but not CInt, so it's generally more convenient to convert from the C types. To convert, I suppose you could either write a <code>checkedFromIntegral</code> function if you're sure it's small or just use Integer.
+
The Haskell report only guarantees that Int has 30 bits of signed precision, so converting CInt to Int is not safe! On the other hand, many classes have instances for Int and Integer but not CInt, so it's generally more convenient to convert from the C types. To convert, I suppose you could either write a <code>checkedFromIntegral</code> function if you're sure it's small or just use Integer.
   
 
For details on impure functions, pointers to objects, etc., see the cookbook.
 
For details on impure functions, pointers to objects, etc., see the cookbook.
Line 41: Line 42:
 
== Marshalling and unmarshalling arguments ==
 
== Marshalling and unmarshalling arguments ==
   
See the cookbook. It's nicer to do the marshalling and unmarshalling in haskell, but it's still low-level repetetive stuff. The functions are all available below Foreign, which supports memory allocation and pointers (and hence C arrays and "out" parameters). One thing it ''doesn't'' support is structs.
+
See the cookbook. It's nicer to do the marshalling and unmarshalling in Haskell, but it's still low-level repetitive stuff. The functions are all available below Foreign, which supports memory allocation and pointers (and hence C arrays and "out" parameters). One thing it ''doesn't'' support is structs.
   
Tools like GreenCard were created to help with this (as well as the low-level boilerplate thing).
+
Tools like [[c2hs]] were created to help with this (as well as the low-level boilerplate thing).
   
[ ''TODO: more detail here? examples in greencard?'' ]
+
[ ''TODO: more detail here? examples in c2hs?'' ]
   
 
== Compiling FFI-using modules ==
 
== Compiling FFI-using modules ==
Line 60: Line 61:
 
</pre>
 
</pre>
   
Notice the use of _dummy_target and --make. The idea is that you get make to compile what is necessary for C, and then always run ghc with --make, at which point it will figure out what is necessary to compile for haskell.
+
Notice the use of _dummy_target and --make. The idea is that you get make to compile what is necessary for C, and then always run ghc with --make, at which point it will figure out what is necessary to compile for Haskell.
   
 
Actually, this is broken, because ghc --make will not notice if a .o file has changed!
 
Actually, this is broken, because ghc --make will not notice if a .o file has changed!

Latest revision as of 19:09, 21 August 2013


Haskell's FFI is used to call functions from other languages (basically C at this point), and for C to call Haskell functions.

Contents

[edit] 1 Links

[edit] 2 Short version

There are many more useful examples in the FFI cook book, but here's a few basic ones:

{-# INCLUDE <math.h> #-}
{-# LANGUAGE ForeignFunctionInterface #-}
module FfiExample where
import Foreign.C -- get the C types
 
-- pure function
foreign import ccall "sin" c_sin :: CDouble -> CDouble
sin :: Double -> Double
sin d = realToFrac (c_sin (realToFrac d))

Note that the FFI document recommends putting the header in the double quotes, like

foreign import ccall "math.h sin" c_sin :: CDouble -> CDouble

GHC since 6.10.x ignores both the INCLUDE pragma (equivalently command line -#include) and the header in the double quotes. GHC 6.8.x and before prefers the INCLUDE pragma (equivalently command line -#include) and in Cabal package descriptions. Other compilers probably prefer the header in the double quotes (if they compile via C) or ignore (if they do not compile via C)—check their documentations.

Notice that C types are not the same as Haskell types, and you have to import them from Foreign.C. Notice also that, as usual in Haskell, you have to explicitly convert to and from Haskell types. Using c_<name_of_c_function> for the raw C function is just my convention.

The Haskell report only guarantees that Int has 30 bits of signed precision, so converting CInt to Int is not safe! On the other hand, many classes have instances for Int and Integer but not CInt, so it's generally more convenient to convert from the C types. To convert, I suppose you could either write a checkedFromIntegral function if you're sure it's small or just use Integer.

For details on impure functions, pointers to objects, etc., see the cookbook.

[edit] 3 Marshalling and unmarshalling arguments

See the cookbook. It's nicer to do the marshalling and unmarshalling in Haskell, but it's still low-level repetitive stuff. The functions are all available below Foreign, which supports memory allocation and pointers (and hence C arrays and "out" parameters). One thing it doesn't support is structs.

Tools like c2hs were created to help with this (as well as the low-level boilerplate thing).

[ TODO: more detail here? examples in c2hs? ]

[edit] 4 Compiling FFI-using modules

[edit] 4.1 GHC

Here's a makefile fragment to compile an FfiExample module that uses C functions from c_functions.c, which uses library functions from libcfuncs:

HFLAGS=-I/path/to/lib/include -L/path/to/lib

_dummy_target: c_functions.o c_functions.h
  ghc $(HFLAGS) -main-is FfiExample --make -o ffi_example c_functions.o -lcfuncs

Notice the use of _dummy_target and --make. The idea is that you get make to compile what is necessary for C, and then always run ghc with --make, at which point it will figure out what is necessary to compile for Haskell.

Actually, this is broken, because ghc --make will not notice if a .o file has changed!

[ this is just my hack, anyone have a better way to do this? ]

[edit] 4.2 Other compilers

Fill me in!

[edit] 5 Complete example with GHC

GHC's libs don't (apparently?) support generic termios stuff. I could implement the whole tcgetattr / tcsetattr thing, but let's just turn ICANON on and off, so IO.getChar doesn't wait for a newline:

termops.c:

#include <termios.h>
#include "termops.h"

void
set_icanon(int fd)
{
        struct termios term;
        tcgetattr(0, &term);
        term.c_lflag |= ICANON;
        tcsetattr(fd, TCSAFLUSH, &term);
}


void
unset_icanon(int fd)
{
        struct termios term;
        tcgetattr(0, &term);
        term.c_lflag &= ~ICANON;
        tcsetattr(fd, TCSAFLUSH, &term);
}

termops.h:

void set_icanon(int fd);
void unset_icanon(int fd);

Termios.hs:

{-# INCLUDE <termios.h> #-}
{-# INCLUDE "termops.h" #-}
{-# LANGUAGE ForeignFunctionInterface #-}
module Termios where
import Foreign.C
 
foreign import ccall "set_icanon" set_icanon :: CInt -> IO ()
foreign import ccall "unset_icanon" unset_icanon :: CInt -> IO ()

FfiEx.hs:

module FfiEx where
import Control.Exception
import System.IO
import qualified Termios
import Control.Monad (when)
 
main = bracket_ (Termios.unset_icanon 0) (Termios.set_icanon 0)
    (while_true prompt)
 
while_true op = do
    continue <- op
    when continue (while_true op)
 
prompt = do
    putStr "? "
    hFlush stdout
    c <- getChar
    putStrLn $ "you typed " ++ [c]
    return (c /= 'q')

makefile:

_ffi_ex: termops.o
    ghc --make -main-is FfiEx -o ffi_ex FfiEx.hs termops.o

[this only worked for me when I omitted termops.o at the end of the `ghc --make` command. Seems like it searches for and finds the .o automatically? --lodi ]


And now:


% make
gcc -c -o termops.o termops.c
ghc --make -main-is FfiEx -o ffi_ex FfiEx.hs termops.o
[1 of 2] Compiling Termios          ( Termios.hs, Termios.o )
[2 of 2] Compiling FfiEx            ( FfiEx.hs, FfiEx.o )
Linking ffi_ex ...
% ./ffi_ex
? you typed a
? you typed b
? you typed q
%