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

BAReFOOt

That would be “.hsc” not “.chs”. Maybe that was the problem…

Magnus

Possibly, though I doubt it. Please report back once you’ve tried it out yourself :-)

anon

excellent, thank you

Leave a comment