C and Haskell sitting in a tree…
A few days ago I thougth I’d take a look at calling C functions from haskell. I wrote up the following set of files:
foo.h:
int foo(int i);
foo.c:
int
foo(int i)
{
return i * i;
}
Foo.hs:
module Main where
import Foreign.C.Types
main = do
r <- foo 2
putStrLn $ show r
foreign import ccall safe "foo.h foo" foo :: CInt -> IO CInt
Compiling the C file was of course no problem:
% gcc -c foo.c
The haskell file offered some resistance:
% ghc -c Foo.hs
Foo.hs:9:8: parse error on input `import'
It took me a round on haskell-cafe before I found out that ghc needs to be told to use the foreign function interface, -ffi or -fffi:
% ghc -c -fffi Foo.hs
Linking is a snap after that:
% ghc -o foo foo.o Foo.o
% ./foo
4
It’s also possible to build and link it all in one go:
% ghc --make -fffi -o foo foo.c Foo.hs
Now, that’s pretty nice, however it’d be even nicer to use cabal to do the building. At the same time I decided to put c2hs to use. It seemed to be a lot easier than having to create the import statements manually. I ended up with the following:
csrc/foo.h:
#ifndef _FOO_H_
int foo(int);
#endif
csrc/foo.c:
#include "foo.h"
int
foo(int i)
{
return i * i;
}
I couldn’t get cabal to accept Foo.chs as the file containing the Main module in my project. So I ended up putting all the relevant code in Foo and then have a dummy Main.
src/Foo.chs:
module Foo where
#include "foo.h"
import Foreign.C.Types
main = do
r <- {# call foo #} 2
putStrLn $ show r
Here’s the dummy Main.
src/Main.hs:
module Main where
import qualified Foo
main = Foo.main
The cabal file is rather straight forward. It took me a round on haskell-cafe to find out how to let the compiler know that I need the foreign function interface without putting compiler directives in the source file.
cnh.cabal:
name: cnh
version: 0.1
build-depends: base
executable: cnh
main-is: Main.hs
hs-source-dirs: src
include-dirs: csrc
c-sources: csrc/foo.c
extensions: ForeignFunctionInterface
other-modules: Foo
Nothing special is needed in the Setup.hs:
#! /usr/bin/env runhaskell
import Distribution.Simple
main = defaultMain
Make it executable and you can build in two easy steps:
% ./Setup.hs configure && ./Setup.hs build
That would be “.hsc” not “.chs”. Maybe that was the problem…
Possibly, though I doubt it. Please report back once you’ve tried it out yourself