14

string -> string我想在C# 程序中使用具有以下类型 :: 的 Haskell 函数 。

我想使用hs-dotnet来连接两个世界。作者声称这是可能的,但没有提供此案例的样本。提供的唯一示例是使用 Haskell 的 .NET 的示例。

有没有这种用法的样本,或者如何使用它?(我在桥接程序集上使用了.NET Reflector,但我什么都不懂。)

4

4 回答 4

14

虽然您的方法可行,但值得注意的是,不幸的是您遇到的困难是您自己造成的(而不是GHC中的错误):((以下假设您在构建 DLL 时使用了 GHC 文档并将RTS加载到 DLL main )。

对于第一部分,您提出的内存分配问题,有一种更简单的 C# 本机方式来处理这个问题,这是不安全的代码。在不安全代码中分配的任何内存都将在托管堆之外分配。因此,这将消除对 C 技巧的需要。

第二部分是C#中LoadLibrary的使用。P/Invoke找不到您的导出的原因很简单:在您的 Haskell 代码中,您使用 声明了导出语句ccall,而在 .NET 中,标准命名约定是stdcall,这也是Win32 API 调用的标准。

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);

也应该可以工作,但它不会因为 Marshaller 期望 String 已经被分配 CoTaskMemAlloc 并且会调用 CoTaskMemFree 和crash

如果你想完全留在管理的土地上,你总是可以这样做

[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 是原始的副本。我们必须释放它。在 C# 中执行 GC 时,它不会影响 CWString,它是一个副本。

然而问题是当我们在 Haskell 代码中释放它时,我们不能使用 freeCWString。指针未使用 C (msvcrt.dll) 的 alloc 分配。有三种方法(我知道)可以解决这个问题。

  • 调用 Haskell 函数时,在 C# 代码中使用 char* 而不是 String。然后,当您调用返回时,您将获得指向 free 的指针,或者使用fixed初始化指针。
  • 在 Haskell 中导入CoTaskMemFree并在 Haskell 中释放指针
  • 使用 StringBuilder 而不是 String。我对此并不完全确定,但想法是,由于 StringBuilder 是作为本机指针实现的,Marshaller 只需将此指针传递给您的 Haskell 代码(顺便说一句,它也可以更新它)。在调用返回后执行 GC 时,应释放 StringBuilder。
于 2010-05-06T11:44:23.677 回答
4

就像更新一样,我通过制作一个haskell DLL并以这种方式桥接两个世界来解决了这个问题。

如果你想走同样的路,一定要使用::CoTaskMemAlloc为.net 世界分配数据。另一个问题是 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 回答