13

私は C のライブラリに FFI モジュールを作成しています。これは、1 回限りの再入不可の関数を他の何よりも先に呼び出す必要があります。この呼び出しはべき等ですが、ステートフルなので、すべての Haskell 呼び出しで呼び出すことができます。しかし、これは遅く、再入不可であるため、競合が発生する可能性があります。

では、これは unsafePerformIO を使用する適切な時期でしょうか? 安全でない IORef または MVar で Bool をラップして、後続の呼び出し (グローバルな非表示の IORef 状態が False である呼び出し) を無視することにより、これらの初期化呼び出しをべき等にすることができます。

そうでない場合、これを行う正しい方法は何ですか?

4

2 に答える 2

11

私は、一度初期化して、マシンを初期化した証拠として偽造不可能なトークンを提供するアプローチを好みます。

したがって、あなたの証拠は次のようになります。

data Token = Token

抽象的にエクスポートします。

その後、初期化関数はこの証拠を返すことができます。

init :: IO Token

次に、その証明を API に渡す必要があります。

bar  :: Token -> IO Int
bar !tok = c_call_bar

このようなものをモナドまたは高次の初期化環境でラップして、よりクリーンにすることができますが、それが基本的な考え方です。

隠し状態を使用して C ライブラリを初期化する際の問題は、ライブラリへのアクセスを並列化できないか、コンパイルされたコードとバイトコードが混在している GHCi で問題が発生し、C ライブラリの 2 つの異なるバージョンがロードされていることです (これは次のように失敗します)。リンカ エラー)。

于 2013-01-03T19:02:39.793 に答える
2

現在、Neil Mitchell によって、(「結果の IO アクションが実行されたときに、その引数が弱いヘッドの通常の形式に評価されるように強制します。」)に基づいて、またはその代わりに、いくつかの新しいトリックが提案されていることに注意してください。withSocketsDo evaluate

withSocketsDo act = do evaluate withSocketsInit; act 

{-# NOINLINE withSocketsInit #-}
withSocketsInit = unsafePerformIO $ do
    initWinsock
    termWinsock

withSocketsDo を呼び出す必要をなくすための私のアプローチは、それを非常に安価にし、それを必要な場所に散りばめることでした。

必ずしもこれは美しいアイデアではありません...

(ライブラリでこの更新を発表する彼の回答も参照してください。)

于 2015-02-26T21:20:33.130 に答える