gpt4 book ai didi

.net - 在 .NET 中调用 Haskell 函数

转载 作者:行者123 更新时间:2023-12-03 04:16:01 25 4
gpt4 key购买 nike

我想使用以下类型的 Haskell 函数::来自 C# 程序的 string -> string

我想使用hs-dotnet架起两个世界的桥梁。作者声称这是可能的,但没有提供这种情况的样本。提供的唯一示例是使用 Haskell 的 .NET 的示例。

有这种用法的示例吗?或者如何使用它? (我在桥接组件上使用了.NET Reflector,但我什么也不明白。)

最佳答案

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

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

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

stdcallccall 在参数清理方面具有不同的名称修饰和职责。

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

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

  • 调用 Haskell 函数时,在 C# 代码中使用 char* 而不是 String。然后,当您调用 return 时,您就可以将指针释放,或者使用 fixed 初始化指针。 .
  • 导入CoTaskMemFree在 Haskell 中并释放 Haskell 中的指针
  • 使用 StringBuilder 而不是 String。我对此并不完全确定,但想法是,由于 StringBuilder 是作为 native 指针实现的,Marshaller 只是将此指针传递给您的 Haskell 代码(顺便说一句,它也可以更新它)。当调用返回后执行GC时,StringBuilder应该被释放。

关于.net - 在 .NET 中调用 Haskell 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1494478/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com