6

我正在尝试编写一个与 C 通信的 Haskell 程序(最终通过 GHC-iOS 用于 iOS)。我希望它将一个字符串从 C 传递给 Haskell,让 Haskell 处理它,然后通过 hsc2s 从 Haskell 返回一些数据类型到 C Structs。我一直未能找到一个清晰、简单的教程。Haskell 唯一需要 C 的东西就是字符串,没有别的。

我对第一部分没有任何问题,将字符串传递给 Haskell。

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

出于测试目的和未来参考,我只想能够处理以下内容。

C结构

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

Haskell 数据类型

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

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

我知道我需要将 TestData 类型分类为 Storable 并实现 peek、poke、sizeOf 和对齐,但我需要先看一个简单的例子才能真正理解它。大多数教程都需要外部库,并使其变得比需要的更复杂。

以下是我看过的资源:

Stackoverflow - 如何使用 hsc2hs 绑定到常量、函数和数据结构?

Haskell Cafe - 适合初学者的 FFI

用 C 代码编写 Haskell 接口:hsc2hs

Haskell Wikibook - FFI

编辑:目前我被卡住了(在 C 中调用 setFoo 时出现分段错误)

Haskell 代码片段

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 代码片段

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);
4

1 回答 1

13

这是一个完整的 C 程序示例,它将结构传递给 Haskell,然后 Haskell 改变该结构的值。它没有外部依赖。如果你有 GHC,你应该能够复制代码并运行它。希望这将为其他人提供一个简单、直接的示例。

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 ()

主程序

#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();
}

命令行- 编译文件并运行

$ hsc2hs HsFoo.hsc
$ ghc -c HsFoo.hs foo.c
$ ghc -no-hs-main foo.c HsFoo.o main.c -o main
$ ./main
于 2015-05-28T08:07:59.387 回答