Haskellの構造体をCと同期させる
Haskellの代数的データ型とCの構造体を通信するためにhsc2hsというツールを使うと便利らしい。次のHogeという構造体でテストしてみた。
サンプルコード
// Types.h struct Hoge { uint32_t field; };
-- Types.hsc {-# LANGUAGE ForeignFunctionInterface #-} #include "Types.h" data Hoge = Hoge { field :: UINT } deriving (Show,Eq) instance Storable Hoge where sizeOf = #size struct Hoge alignment = sizeOf peek (Hoge x) = (#peek struct Hoge, field) x poke ptr (Hoge x) = (#poke struct Hoge, field) ptr x
コード生成
C:\usr\src\test>hsc2hs Types.hsc
これで次のようなソースを生成します。
-- Types.hs {-# INCLUDE "Types.h" #-} {-# LINE 1 "Types.hsc" #-} {-# LANGUAGE ForeignFunctionInterface #-} {-# LINE 2 "Types.hsc" #-} {-# LINE 4 "Types.hsc" #-} data Hoge = Hoge { field :: UINT } deriving (Show,Eq) instance Storable Hoge where sizeOf = (4) {-# LINE 9 "Types.hsc" #-} alignment = sizeOf peek (Hoge x) = ((\hsc_ptr -> peekByteOff hsc_ptr 0)) x {-# LINE 11 "Types.hsc" #-} poke ptr (Hoge x) = ((\hsc_ptr -> pokeByteOff hsc_ptr 0)) ptr x {-# LINE 12 "Types.hsc" #-}
データの流れ
hsc2hsは次のような流れで実行しているようですね。
*.hsc →[hsc2hs]→ *_make.c →[gcc]→ *.exe →[実行]→ *.hs
hsc2hsに--no-compileというオプションを付けて実行すると、次のようなソースを出力します。
#include "C:/usr/bin/Haskell Platform/2010.2.0.0/lib/template-hsc.h" #line 3 "Types.hsc" #include "Types.h" int main (int argc, char *argv []) { #if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 409 printf ("{-# OPTIONS -optc-D__GLASGOW_HASKELL__=%d #-}\n", __GLASGOW_HASKELL__); #endif #if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 603 printf ("{-# OPTIONS -#include %s #-}\n", "\"Types.h\""); #elif __GLASGOW_HASKELL__ < 610 printf ("{-# INCLUDE %s #-}\n", "\"Types.h\""); #endif hsc_line (1, "Types.hsc"); fputs ("{-# LANGUAGE ForeignFunctionInterface #-}\n" "", stdout); hsc_line (2, "Types.hsc"); fputs ("\n" "", stdout); fputs ("\n" "", stdout); hsc_line (4, "Types.hsc"); fputs ("\n" "data Hoge = Hoge { field :: UINT } deriving (Show,Eq)\n" "\n" "instance Storable Hoge where\n" " sizeOf = ", stdout); #line 8 "Types.hsc" hsc_size (struct Hoge); fputs ("\n" "", stdout); hsc_line (9, "Types.hsc"); fputs (" alignment = sizeOf\n" " peek (Hoge x) = (", stdout); #line 10 "Types.hsc" hsc_peek (struct Hoge, field); fputs (") x\n" "", stdout); hsc_line (11, "Types.hsc"); fputs (" poke ptr (Hoge x) = (", stdout); #line 11 "Types.hsc" hsc_poke (struct Hoge, field); fputs (") ptr x\n" "", stdout); hsc_line (12, "Types.hsc"); fputs ("", stdout); return 0; }
1行目に書いてあるtemplate-hsc.hの中を見てみると、こんな感じでマクロが色々と定義されていました。
#define hsc_size(t) \ printf("(%ld)", (long) sizeof(t));
要するにアラインメントとかの計算は全部gccに任せちゃってるのか。なるほど。
あと、この自動生成されるソースはCPPではなくCなので、hsc で書くときに構造体の型を "struct Hoge" と宣言してあげるか、ヘッダのほうで typedef とか使ってやらないとCのソースをコンパイルするときにエラーが出るので注意しなければなりません。