プリプロセッサの使用を強くお勧めします。私は c2hs が好きですが、hsc2hs は ghc に含まれているため非常に一般的です。グリーンカードは放棄されたようです。
質問に答えるには:
1) はい、Storable インスタンスの定義を通じて。Storable を使用することは、FFI を介してデータを渡すための唯一の安全なメカニズムです。Storable インスタンスは、Haskell 型と生メモリ (Haskell Ptr、ForeignPtr、StablePtr、または C ポインター) の間でデータをマーシャリングする方法を定義します。次に例を示します。
data PlateC = PlateC {
numX :: Int,
numY :: Int,
v1 :: Double,
v2 :: Double } deriving (Eq, Show)
instance Storable PlateC where
alignment _ = alignment (undefined :: CDouble)
sizeOf _ = {#sizeof PlateC#}
peek p =
PlateC <$> fmap fI ({#get PlateC.numX #} p)
<*> fmap fI ({#get PlateC.numY #} p)
<*> fmap realToFrac ({#get PlateC.v1 #} p)
<*> fmap realToFrac ({#get PlateC.v2 #} p)
poke p (PlateC xv yv v1v v2v) = do
{#set PlateC.numX #} p (fI xv)
{#set PlateC.numY #} p (fI yv)
{#set PlateC.v1 #} p (realToFrac v1v)
{#set PlateC.v2 #} p (realToFrac v2v)
{# ... #}フラグメントは c2hs コードです 。fIですfromIntegral。get フラグメントと set フラグメントの値は、同名の Haskell 型ではなく、インクルードされたヘッダーから次の構造体を参照します。
struct PlateCTag ;
typedef struct PlateCTag {
int numX;
int numY;
double v1;
double v2;
} PlateC ;
c2hs はこれを次の単純な Haskell に変換します。
instance Storable PlateC where
alignment _ = alignment (undefined :: CDouble)
sizeOf _ = 24
peek p =
PlateC <$> fmap fI ((\ptr -> do {peekByteOff ptr 0 ::IO CInt}) p)
<*> fmap fI ((\ptr -> do {peekByteOff ptr 4 ::IO CInt}) p)
<*> fmap realToFrac ((\ptr -> do {peekByteOff ptr 8 ::IO CDouble}) p)
<*> fmap realToFrac ((\ptr -> do {peekByteOff ptr 16 ::IO CDouble}) p)
poke p (PlateC xv yv v1v v2v) = do
(\ptr val -> do {pokeByteOff ptr 0 (val::CInt)}) p (fI xv)
(\ptr val -> do {pokeByteOff ptr 4 (val::CInt)}) p (fI yv)
(\ptr val -> do {pokeByteOff ptr 8 (val::CDouble)}) p (realToFrac v1v)
(\ptr val -> do {pokeByteOff ptr 16 (val::CDouble)}) p (realToFrac v2v)
もちろん、オフセットはアーキテクチャに依存するため、プリプロセッサを使用すると、移植可能なコードを記述できます。
これを使用するには、データ型 ( 、 など) にスペースを割り当てnew、mallocデータpokeを Ptr (または ForeignPtr) に入れます。
2) これは実メモリ レイアウトです。
peek3) /による読み書きにはペナルティがありpokeます。大量のデータがある場合は、配列全体を Haskell リストにマーシャリングする代わりに、C 配列から 1 つの要素だけを読み取るなど、必要なものだけを変換する方が適切です。
4) 構文は、選択したプリプロセッサによって異なります。 c2hs ドキュメント. hsc2hs ドキュメント. 紛らわしいことに、hsc2hs は構文#stufforを使用しますが、c2hs は#{stuff}を使用します{#stuff #}。
5) @sclv の提案は、私も同様です。Storable インスタンスを作成し、データへのポインタを保持します。C 関数を記述してすべての作業を行い、それらを FFI を介して呼び出すか、(あまり良くない) peek と poke を使用して低レベルの Haskell を記述し、必要なデータの部分だけを操作することができます。全体をマーシャリングする (つまり、データ構造全体を呼び出すpeek、または呼び出すpoke) とコストがかかりますが、ポインターを渡すだけであれば、コストは最小限に抑えられます。
FFI を介してインポートされた関数を呼び出すと、「安全でない」とマークされていない限り、重大なペナルティがあります。インポートを「安全ではない」と宣言することは、関数が Haskell または未定義の動作結果にコールバックしてはならないことを意味します。同時実行または並列処理を使用している場合、同じ機能 (つまり CPU) 上のすべての Haskell スレッドが呼び出しが戻るまでブロックされることも意味するため、呼び出しはかなり迅速に戻るはずです。これらの条件が受け入れられる場合、「安全でない」呼び出しは比較的高速です。
Hackage には、この種のことを扱うパッケージがたくさんあります。hsndfileとhCsoundは、c2hs の優れた実践例としてお勧めできます。ただし、慣れ親しんだ小さな C ライブラリへのバインディングを見れば、おそらく簡単です。