Haskell Bindings to C – c2hs September 22, 2009

When making hsXenCtrl bindings I was thrilled to learn and use hsc2hs – I considered it all that was needed for sugar on top of the FFI. However, the recent Kernel Modules work has opened my eyes to various limitations of hsc2hs and forced me to learn c2hs – which is superior so I’ll go though a simple-stupid tutorial here.

This is intended as a starter companion to the official documentation – not a substitute! Read the docs, they are quite understandable!

The C Code

Lets consider some basic C code:

struct test { int a; }; struct test *getTheStruct(); struct test t; struct test *getTheStruct() { return &t; } #define add_one(x) (x + 1)

While there are the normal issues of calling functions and accessing structure fields, it’s no coincidence that this code embodies the two issues I came across when using c2hs. First, it has a macro, for which there is no automatic FFI generation, and secondly it has structures that aren’t typedef’ed to a single keyword. We’ll see how to handle both those along with the rest of our simple chs code.

The .chs File

We now write a test.chs file that will interface with the code defined in our c header.

{-# LANGUAGE ForeignFunctionInterface #-} import Foreign.C.Types import Foreign.Ptr import Foreign.Storable #include "test.h"

The boiler plate shouldn’t be surprising – the only new aspect is the inclusion of the header file that defines the types and functions to which we must bind.

Now lets define a pointer type. Unfortunately this code doesn’t use any typedefs and c2hs can’t parse “struct foo” as a single C type identifier. To fix this we use a section of C code, which will appear in the file “test.chs.h” once we run c2hs:

#c typedef struct test test_t; #endc

We may now define the pointer types using “test_t” instead of “struct test”

{#pointer *test_t as Test#}

This is amazingly simple – it translates into “type Test = Ptr ()”. You can add the keyword “newtype” at the end to obtain type enforcement if you so desire.

To access fields in this structure you could define a whole storable instance, using c2hs keywords ‘sizeof’, ‘get’, and ‘put’ the whole way – but we didn’t create a ‘newtype’ on which a typeclass instance can be defined. All we need are a pair of get and set functions – but you can see how to use these and thus create any Storable instance you want.

getA :: Test -> IO Int

getA t = {#get test_t->a#} t >>= return . fromIntegral

setA :: Test -> Int -> IO ()

setA t i = {#set test_t->a#} t (fromIntegral i)

As you can see, use the {#get …} and {#set …} hooks along with the C type (“test_t”) and C field name(s) (“a”) to create a function that will access the types appropriately. Now we need to handle the damned macro. There exists no {#macro …} hook, and function hooks translate directly to “foreign import …” so the first step is using another C section to make a function wrap the macro:

#c int add_one_(int x) { return (add_one(x)); } #endc

This doesn’t warrant any more discussion, so lets move onto the function calls! The function hook is hands-down ugly, but powerful. First you tell it the C function you’re calling (add_one_). Then specify the name of the Haskell function; hat (“^”) means convert the C name to camel case (addOne), or you can specify any name you desire (ex: “hs_add_one”). After the naming is handled then you provide a list of arguments in curry braces – {arg1, arg2, …, argn} – then an arrow and a result Type. Each argument and return value is a Haskell type and enclosed in a back tick and single quote, such as `Int’. Before and after each argument you can provide in and out marshalling functions – this is where all the power of c2hs is and is also well stated in the documentation so I won’t be repeating it here – just notice that I marshal CInt to and from Int via “fromIntegral” and no marshalling is needed for the pointer (“Test”) so we use identity (“id”).

{#fun add_one_ as ^ {fromIntegral `Int' } -> `Int' fromIntegral#}

{#fun get_the_struct as ^ {} -> `Test' id#}

The remaining task is to define a simple ‘main’ function for testing purposes.