私の質問は、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に解決していますが、これは必ずしも格納可能ではありません。
私の質問は、引き続き通信できる、ハスケルでコールバックコードを記述するための最良の方法です。
私は決してFFIのエキスパートではありませんが、Cの人々は本当に閉鎖していないので、「void *」トリックを使用していました。ハスケルでは、本当に閉鎖しているので、 'void *'引数をHaskellインターフェースの外に置いて、局所的なデータ(おそらく 'IORef'や' MVar')を部分的に閉じることができます。 –
ああ!ゴッチャ。私はそれを試してみましょう。私はそれがバインディングがしているかもしれないと思うが、私はそれをキャッチしませんでした。レスポンスありがとう! –
@tigger、私はDanielWagnerがCからHaskellへの同期コールバックを提案する前に、同じトリックを行いました - MVar引数を適用して部分的な関数を取得し、C関数にMVarのデータを返すようにしましょう。 MVarがより複雑な場合は、Storableベクトルまたはストア可能なインスタンスを使用して、CからMVarにデータを渡すことができます。PtrをStorableインスタンスにCに渡します。ここの例:http://hpaste.org/63702 – Sal