2008-05-02

Foreign Function Interface in Haskell

I'm messing around learning to make foreign function interfaces in Haskell. It was sufficiently annoying that I decided to actually document what I did. I picked something, anything. It didn't have to be useful, and I would certainly not implement it in a sane way. The point is to get a chance to try various aspects of the Haskell FFI. I settled just boolean operator evaluation. And not even any boolean operations, just integer operations. So to start, the C header:


/* c_boolean.h */
typedef int (*Operation)(int, int);

int eval(Operation o, int a, int b);
int plus(int a, int b);
int toString(int a, char *buffer, int bufferLen);
void print(char *buffer, int bufferLen);


Pretty simple, define the binary operation as Operation, create a function to evaluate an operation (although really, that's unnecessary but the point of the exercise is to try different things so I had to included function pointers). Then define a simple operation plus, then to add some string manipulation and Haskell managed memory a toString function that converts the int to a string. And finally, a function that returns nothing but actually does something, print.

The C code is pretty dull:


/* c_boolean.c */
#include <stdio.h>
#include "c_boolean.h"

int eval(Operation o, int a, int b) {
return o(a, b);
}

int plus(int a, int b) {
return a+b;
}

int toString(int a, char *buffer, int bufferLen) {
return snprintf(buffer, bufferLen, "%d", a);
}

void print(char *buffer, int bufferLen) {
printf("%s", buffer);
flush(stdout);
}



Like I said, boring. But the interesting stuff is coming up, the Haskell interface to the C code. To write the code, I referred to several places:
http://www.haskell.org/haskellwiki/FFI_Introduction
http://blog.danieroux.com/2007/01/01/simple-demonstration-of-haskell-ffi/
and of course the standard Foreign API functions in:
http://haskell.org/ghc/docs/latest/html/libraries/index.html


-- boolean.hs
import Foreign
import Foreign.C.Types
import Foreign.C.String
import Foreign.Ptr

type Operation = CInt -> CInt -> IO CInt
type C_Operation = FunPtr Operation
foreign import ccall "wrapper"
mkOperation :: Operation -> IO (FunPtr Operation)

foreign import ccall "c_boolean.h eval"
c_eval :: C_Operation -> CInt -> CInt -> IO CInt
my_eval :: Operation -> CInt -> CInt -> IO CInt
my_eval o a b = do
co <- mkOperation o c_eval co a b

foreign import ccall "c_boolean.h plus"
c_plus :: CInt -> CInt -> IO CInt
foreign import ccall "c_boolean.h toString"
c_toString :: CInt -> (Ptr CChar) -> CInt -> IO CInt

with_toString :: Int -> (CStringLen -> IO a) -> CInt -> IO a
with_toString l f n = allocaArray l runF
where
runF str = do
len <- c_toString (fromIntegral n) str n
f (str, l)

foreign import ccall "c_boolean.h print"
c_print :: (Ptr CChar) -> CInt -> IO ()

my_print :: CStringLen -> IO ()
my_print (s, n) = c_print s (fromIntegral n)

minus :: CInt -> CInt -> IO CInt
minus a b = return (a-b)

main = do
cp <- my_eval c_plus 2 6
m <- my_eval minus 5 2
sequence_ [ putStr "C plus: ",
with_toString 10 my_print cp,
putStr "\nHaskell minus: ",
with_toString 10 my_print m,
putStr "\n" ]


Then the compilation and running:

$ gcc -I. c_boolean.c -c && ar rc c_boolean.a c_boolean.o && ranlib c_boolean.a && ghc -L. -lc_boolean -fglasgow-exts -ffi --make boolean.hs -o boolean
[1 of 1] Compiling Main ( boolean.hs, boolean.o )
Linking boolean ...
$ ./boolean
C plus:
Haskell minus:
83$


One problem, even though I used sequence_ and flushed the printf in C, the 8 and 3 are printed at the very end.

Update, fixed formatting.