2012-02-14 18 views
7

私の質問は、Cコードから呼び出せるコールバックをモデル化するフレンドリーなHaskell Interfacesを書く方法です。コールバックはここ(HaskellWiki)で扱われますが、私はこの質問がそのリンクの例よりも複雑であると信じています。FFI Haskellコールバックwith状態

は、我々は、コールバックを必要とする、Cのコードがあると仮定し、ヘッダーは次のようになります。

typedef int CallbackType(char* input, char* output, int outputMaxSize, void* userData) 

int execution(CallbackType* caller); 

この場合関数executionが閉鎖本質的に、コールバック関数を取得し、新たなデータを処理するためにそれを使用します。コールバックには、入力文字列、つまりサイズがoutputMaxSizeで割り当てられた出力バッファと、コールバック内でキャストできるuserDataポインタが必要です。

私たちはMVarsを使ってクロージャを回すときに、同様のことをhaskellで行います。したがって、外部インターフェイスを記述するときは、この種の型を保持したいと考えています。具体的にここに

はFFIコードがどのように見えるかです:

type Callback = CString -> CString -> CInt -> Ptr() -> IO CInt 

foreign import ccall safe "wrapper" 
    wrap_callBack :: Callback -> IO (FunPtr Callback) 

foreign import ccall safe "execution" 
    execute :: FunPtr Callback -> IO CInt 

ユーザーは、この種のものを行うことができる必要がありますが、それは彼らがタイプPTRを持つコールバックを記述する必要が ので、貧しいインターフェースのように感じています()。むしろ、より自然な感じのMVars と交換したいと考えています。だから我々は、関数を記述したいと思います:castCallbackが大部分である。この場合、

castCallback :: (String -> Int -> MVar a -> (Int, String)) 
      -> (CString -> CString -> CInt -> Ptr() -> IO CInt) 

main = wrap_callBack (castCallback myCallback) >>= execute 

myCallback :: String -> Int -> MVar a -> (Int, String) 
myCallback input maxOutLength data = ... 

Cに変換するために、我々は次のように機能を持っているしたいのですが 変換文字列 - > cstring、Int - > CInt、および出力文字列をコピーします。

しかし、難しい部分はMVarをPtrに解決していますが、これは必ずしも格納可能ではありません。

私の質問は、引き続き通信できる、ハスケルでコールバックコードを記述するための最良の方法です。

+0

私は決してFFIのエキスパートではありませんが、Cの人々は本当に閉鎖していないので、「void *」トリックを使用していました。ハスケルでは、本当に閉鎖しているので、 'void *'引数をHaskellインターフェースの外に置いて、局所的なデータ(おそらく 'IORef'や' MVar')を部分的に閉じることができます。 –

+0

ああ!ゴッチャ。私はそれを試してみましょう。私はそれがバインディングがしているかもしれないと思うが、私はそれをキャッチしませんでした。レスポンスありがとう! –

+0

@tigger、私はDanielWagnerがCからHaskellへの同期コールバックを提案する前に、同じトリックを行いました - MVar引数を適用して部分的な関数を取得し、C関数にMVarのデータを返すようにしましょう。 MVarがより複雑な場合は、Storableベクトルまたはストア可能なインスタンスを使用して、CからMVarにデータを渡すことができます。PtrをStorableインスタンスにCに渡します。ここの例:http://hpaste.org/63702 – Sal

答えて

9

MVarのようなHaskell構造体にアクセスしてポインタ表現に変換するライブラリ関数を持たない場合(つまり、Cに渡すべきではない)、部分関数アプリケーションを実行する必要がある。

部分関数のアプリケーションでは、既に適用されているMVarを使用して部分関数を作成し、その関数へのポインタをCに渡します。次に、Cを使ってMVarに入れるオブジェクトを呼び出します。あなたのMVARオブジェクトが複雑な場合

-- this is the function that C will call back 
syncWithC :: MVar CInt -> CInt -> IO() 
syncWithC m x = do 
       putMVar m x 
       return() 

foreign import ccall "wrapper" 
    syncWithCWrap :: (CInt -> IO()) -> IO (FunPtr (CInt -> IO())) 

main = do 
    m <- newEmptyMVar 
    -- create a partial function with mvar m already applied. Pass to C. C will back with CInt 
    f <- syncWithCWrap $ syncWithC m 

何: - 以下の例のコードは、(私はここで例のためにそれを変更しますが変更をテストしていない以下のすべてのコードは、私が前にいた何かに由来していますか)?次に、存在しない場合は、MVarオブジェクトのStorableインスタンスを構築する必要があります。私はint型のペアの配列とMVARを使用したい場合たとえば、最初のIntペアのStorableインスタンスを定義(SVMSVStorable Mutable Vectorで、Storable Vectorです):

data VCInt2 = IV2 {-# UNPACK #-} !CInt 
        {-# UNPACK #-} !CInt 

instance SV.Storable VCInt2 where 
    sizeOf _ = sizeOf (undefined :: CInt) * 2 
    alignment _ = alignment (undefined :: CInt) 
    peek p = do 
      a <- peekElemOff q 0 
      b <- peekElemOff q 1 
      return (IV2 a b) 
    where q = castPtr p 
    {-# INLINE peek #-} 
    poke p (IV2 a b) = do 
      pokeElemOff q 0 a 
      pokeElemOff q 1 b 
    where q = castPtr p 
    {-# INLINE poke #-} 

、あなただけ渡すことができますベクトルへのポインタをCに渡し、ベクトルを更新して、引数なしでvoid関数をコールバックします(Cは既にベクトルを埋めているため)。それは知っているように、これはまた、C側ではハスケルとC.

-- a "wrapper" import is a converter for converting a Haskell function to a foreign function pointer 
foreign import ccall "wrapper" 
    syncWithCWrap :: IO() -> IO (FunPtr (IO())) 


-- call syncWithCWrap on syncWithC with both arguments applied 
-- the result is a function with no arguments. Pass the function, and 
-- pointer to x to C. Have C fill in x first, and then call back syncWithC 
-- with no arguments 
syncWithC :: MVar (SV.Vector VCInt2) -> MSV.IOVector VCInt2 -> IO() 
syncWithC m1 x = do 
       SV.unsafeFreeze x >>= putMVar m1 
       return() 

の間でメモリを共有することにより、高価なデータマーシャリングを避けるため、あなたはそれを解析する方法をVCInt2のための構造体宣言が必要になりますので、

/** Haskell Storable Vector element with two int members **/ 
typedef struct vcint2{ 
    int a; 
    int b; 
} vcint2; 

、C側では、MVarオブジェクトのポインタvcint2を渡しています。

関連する問題