hsc2hs:使用Haskell改变C结构
我正在尝试编写一个与C通信的Haskell程序(最终通过GHC-iOS为iOS)。 我希望它将一个字符串从C传递给Haskell,让Haskell处理它,然后通过hsc2s将一些数据类型从Haskell返回到C Structs。 我找不到一个清晰,简单的教程是不成功的。 Haskell唯一需要的是String,没有别的。
我对第一部分没有任何问题,将字符串传递给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和alignment,但我需要先看一个简单的例子才能真正理解它。 大多数教程都需要外部库,并使其比需要的更复杂。
以下是我看过的资源:
Stackoverflow – 如何使用hsc2hs绑定到常量,函数和数据结构?
Haskell Cafe – 初学者的FFI
将Haskell接口写入C代码: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);
这是一个将结构传递给Haskell的C程序的完整示例,然后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 ()
main.c中
#include #include #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:\na: %s\nb: %s\nc: %d\n",f->a,f->b,f->c); setFoo(f); printf("foo has been set in Haskell:\na: %s\nb: %s\nc: %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