14

string -> stringC# プログラムから、次の型 :: で Haskell 関数を使用したいと考えています 。

hs-dotnetを使用して両方の世界を橋渡ししたいと考えています。著者はそれが可能であると主張していますが、このケースのサンプルは提供していません。提供されているサンプルは、Haskell の .NET を使用するサンプルのみです。

この使用例、または使用方法はありますか? (ブリッジ アセンブリで.NET Reflectorを使用しましたが、何もわかりませんでした。)

4

4 に答える 4

14

あなたの方法はうまくいきますが、あなたが遭遇した問題は残念ながらあなた自身の行動によるものであることに注意してください ( GHCのバグではありません) :( (以下では、DLL をビルドするときに GHC ドキュメントを使用し、RTSを DLL main にロードしていると想定しています) )。

最初の部分、あなたが提示するメモリ割り当ての問題については、これを処理するはるかに簡単な C# ネイティブの方法があります。これは安全でないコードです。アンセーフ コードで割り当てられたメモリは、マネージ ヒープの外部に割り当てられます。したがって、これにより、C のトリックの必要性がなくなります。

2 番目の部分は、C# での LoadLibrary の使用です。P/Invokeがエクスポートを見つけられない理由は非常に単純です。Haskell コードでは を使用してエクスポート ステートメントを宣言しましたがccall、.NET では標準の命名規則はであり、これはWin32 API 呼び出しstdcallの標準でもあります。

stdcall引数のクリーンアップに関して、ccall異なる名前マングリングと責任があります。

特に、GHC/GCC は "wEval" をエクスポートしますが、.NET はデフォルトで "_wEval@4" を探します。CallingConvention = CallingConvention.Cdecl を追加するだけで、簡単に修正できます。

ただし、この呼び出し規約を使用すると、呼び出し元はスタックをクリーンアップする必要があります。したがって、追加の作業が必要になります。これを Windows でのみ使用すると仮定すると、Haskell 関数を としてエクスポートするだけstdcallです。これにより、.NET コードが単純になり、

[DllImport("foo.dll", CharSet = CharSet.Unicode)]
public static extern string myExportedFunction(string in);

ほぼ正しい。

正しいのは例えば

[DllImport("foo.dll", CharSet = CharSet.Unicode)]
public unsafe static extern char* myExportedFunction([MarshalAs(UnmanagedType.LPWStr)]string in);

loadLibrary などはもう必要ありません。管理された文字列を取得するには、次を使用します

String result = new String(myExportedFunction("hello"));

例えば。

と思うだろう

[DllImport("foo.dll", CharSet = CharSet.Unicode)]
[return : MarshalAs(UnmanagedType.LPWStr)]
public static extern string myExportedFunction([MarshalAs(UnmanagedType.LPWStr)]string in);

も機能するはずですが、マーシャラーは文字列が CoTaskMemAlloc で割り当てられていると想定し、その上で CoTaskMemFree を呼び出してクラッシュするため、機能しません。

管理された土地に完全にとどまりたい場合は、いつでも行うことができます

[DllImport("foo.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr myExportedFunction([MarshalAs(UnmanagedType.LPWStr)]string in);

そして、それは次のように使用できます

string result = Marshal.PtrToStringUni(myExportedFunction("hello"));

ツールはここから入手できますhttp://hackage.haskell.org/package/Hs2lib-0.4.8

更新: 最近発見した大きな落とし穴があります。.NET の String 型は不変であることを覚えておく必要があります。したがって、マーシャラーがそれを Haskell コードに送信すると、そこで得られる CWString は元のコピーです。これを解放しなければなりません。GC が C# で実行される場合、コピーである CWString には影響しません。

ただし問題は、Haskell コードでこれを解放すると、freeCWString を使用できないことです。ポインターは、C (msvcrt.dll) の割り当てでは割り当てられませんでした。これを解決するには、(私が知っている) 3 つの方法があります。

  • Haskell 関数を呼び出すときは、C# コードで String の代わりに char* を使用します。次に、 return を呼び出すときに解放するポインターを取得するか、 fixedを使用してポインターを初期化します。
  • Haskell でCoTaskMemFreeをインポートし、Haskell でポインターを解放します
  • String の代わりに StringBuilder を使用します。これについては完全にはわかりませんが、StringBuilder はネイティブ ポインターとして実装されているため、マーシャラーはこのポインターを Haskell コードに渡すだけです (更新も可能です)。呼び出しが返された後に GC が実行されると、StringBuilder が解放されます。
于 2010-05-06T11:44:23.677 に答える
4

アップデートとして、haskell DLL を作成し、2 つの世界を橋渡しすることで問題を解決しました。

同じパスを使用::CoTaskMemAllocする場合は、.net ワールドのデータを割り当てるために必ず使用してください。もう 1 つの落とし穴は、LoadLibrary/GetProcAdress の使用です。なんらかの理由で、インポートが想定どおりに自動的に機能しません。.net から haskell を呼び出すための詳細な記事。

于 2009-10-30T13:47:54.103 に答える
2

少なくとも C から Haskell を呼び出すことはできます。Haskell ファイルで「外部エクスポート」を使用すると、GHC が C ヘッダーを生成し、それをインポートして C から Haskell を呼び出すために使用できます。

私はこれが .NET バインディングで行われるのを見たことがないので、作者 (Sigbjorn) と haskell-cafe@ の両方に例を尋ねるのが最善だと思います。

于 2009-09-29T20:37:18.993 に答える
-12

.NET で Haskell が必要な場合は、F#を使用してください。

于 2011-08-14T19:47:57.727 に答える