6
votes

I am trying to write a Haskell program that communicates with C (ultimately for iOS via GHC-iOS). I want it to pass a string from C to Haskell, have Haskell process it and then return some Data types from Haskell to C Structs via hsc2s. I have been unsuccessful at finding a clear, simple tutorial. The only thing Haskell needs from C is the String, nothing else.

I have no trouble with the very first part, passing a string to Haskell.

testPrint :: CString -> IO ()
testPrint c = do 
    s <- peekCString c
    putStrLn s

For test purposes and future reference, I just want to be able to handle something like the following.

C Struct

struct testdata {
   char *a;
   char *b;
   int c;
};

Haskell Data Type

data TestData =  TestData {
  a :: String,
  b :: String,
  c :: Int
} deriving Show

-- not sure what the type would look like
testParse :: CString -> TestData

I understand that I need to typeclass TestData as Storable and implement peek, poke, sizeOf and alignment, but I need to see a simple example before I can really understand it. Most of the tutorials require outside libraries and make it more complicated than it needs to be.

Here are the resources I've looked at:

Stackoverflow - How to use hsc2hs to bind to constants, functions and data structures?

Haskell Cafe - FFI for a beginner

Writing Haskell interfaces to C code: hsc2hs

Haskell Wikibook - FFI

Edit: Currently where I am stuck (gets segmentation error when setFoo is called in C)

Haskell Code Snippet

instance Storable Foo where
  sizeOf = #{size foo}
  alignment = alignment (undefined :: CString)
  poke p foo = do
     #{poke foo, a} p $ a foo
     #{poke foo, b} p $ b foo
     #{poke foo, c} p $ c foo
  peek p = return Foo
    `ap` (#{peek foo, a} p)
    `ap` (#{peek foo, b} p)
    `ap` (#{peek foo, c} p)

foreign export ccall "setFoo" setFoo :: Ptr Foo -> IO ()

setFoo :: Ptr Foo -> IO ()
setFoo f = do
  newA <- newCString "abc"
  newB <- newCString "def"
  poke f (Foo newA newB 123)

C Code Snippet

foo *f;
f = malloc(sizeof(foo));
foo->a = "bbb"
foo->b = "aaa"
foo->c = 1
// this is incorrect
// setFoo(&f);
// this is correct
setFoo(f);
1
I've been working through the first and fourth link. The problem I am running in to now is how to export a function that returns TestData to C. I think it needs to looks like this testParse :: CString -> IO (Ptr TestData) but I am not sure how to wrap TestData into a Ptr. I already have a Storable instance for TestData.MCH
Got it to compile with testParse :: CString -> IO (StablePtr TestData) but I am getting a segmentation fault in C. Maybe it is the way I setup Storable. I'll post code later.MCH
have you tried looking into the source code of existing Haskell FFI C bindings? I'm pretty sure it's not the first time something like this is attempted.Erik Kaplun
I am sure it is not the first time. I have found a bit of code on Github. I think I'll be able to piece something together, seems like the right way to do it is pass a Ptr and mutate the pointed item. Anyway, I'll post my result as answer in the next day or two.MCH
@MCH setFoo(&f) <-- that seems to be wrong. You are passing a pointer to a pointer to foo, that means that caller allocates space for foo. But haskell site doesn't allocate anything, it just initializes the memory. You shoud do setFoo(f).Yuras

1 Answers

12
votes

This is a complete example of a C program that passes a struct to Haskell then Haskell mutates the values of that struct. It has no outside dependencies. You should be able to copy the code and run it if you have GHC. Hopefully this will serve as a simple, straightforward example for others.

Foo.h

typedef struct {
    char *a;
    char *b;
    int   c;
} foo;

Foo.c

#include "foo.h"

HsFoo.hsc

{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE CPP                      #-}

module HsFoo where

import Foreign
import Foreign.C

import Control.Applicative
import Control.Monad

#include "foo.h"

data Foo = Foo { 
    a :: CString
  , b :: CString
  , c :: Int
} deriving Show

instance Storable Foo where
    sizeOf    _ = #{size foo}
    alignment _ = alignment (undefined :: CString)

    poke p foo = do
        #{poke foo, a} p $ a foo
        #{poke foo, b} p $ b foo
        #{poke foo, c} p $ c foo

    peek p = return Foo
              `ap` (#{peek foo, a} p)
              `ap` (#{peek foo, b} p)
              `ap` (#{peek foo, c} p)

foreign export ccall "free_HaskellPtr" free :: Ptr a -> IO ()
foreign export ccall "setFoo" setFoo :: Ptr Foo -> IO ()
setFoo :: Ptr Foo -> IO ()
setFoo f = do
  newA <- newCString "abc"
  newB <- newCString "def"
  poke f $ Foo newA newB 3
  return ()

main.c

#include <stdio.h>
#include <stdlib.h>
#include "HsFoo_stub.h"
#include "foo.h"

int main(int argc, char *argv[]) {  
  hs_init(&argc, &argv);

  foo *f;
  f = malloc(sizeof(foo));
  f->a = "Hello";
  f->b = "World";
  f->c = 55555; 

  printf("foo has been set in C:\n  a: %s\n  b: %s\n  c: %d\n",f->a,f->b,f->c);

  setFoo(f);

  printf("foo has been set in Haskell:\n  a: %s\n  b: %s\n  c: %d\n",f->a,f->b,f->c);

  free_HaskellPtr(f->a);
  free_HaskellPtr(f->b);
  free(f);
  hs_exit();
}

Command Line - Compile files and run

$ hsc2hs HsFoo.hsc
$ ghc -c HsFoo.hs foo.c
$ ghc -no-hs-main foo.c HsFoo.o main.c -o main
$ ./main