C# 从C调用Haskell# 我刚刚花了一个星期左右的时间来计算如何从C++中执行C++代码作为我日常工作的一部分。我们花了很长时间才弄明白,但最终的解决方案相当简单
现在我很好奇。。。从C#给Haskell打电话有多难?(请注意:这是从C#调用Haskell,而不是相反。因此,主可执行文件是C#。) 如果真的很难,我就不麻烦了。但如果它相当容易,我可能不得不玩它 基本上,我们编写了一些C++代码。在Windows上它被编译成DLL,在Linux上它被编译成共享对象(C# 从C调用Haskell# 我刚刚花了一个星期左右的时间来计算如何从C++中执行C++代码作为我日常工作的一部分。我们花了很长时间才弄明白,但最终的解决方案相当简单,c#,haskell,ffi,C#,Haskell,Ffi,现在我很好奇。。。从C#给Haskell打电话有多难?(请注意:这是从C#调用Haskell,而不是相反。因此,主可执行文件是C#。) 如果真的很难,我就不麻烦了。但如果它相当容易,我可能不得不玩它 基本上,我们编写了一些C++代码。在Windows上它被编译成DLL,在Linux上它被编译成共享对象(*.so)。然后在C#端执行一个DllImport,并编写一些手动内存管理代码,如果您试图传递任何不寻常的内容。(例如,数组、字符串等) 我知道GHC应该支持在这两个平台上构建共享库,但我不确定技
*.so
)。然后在C#端执行一个DllImport
,并编写一些手动内存管理代码,如果您试图传递任何不寻常的内容。(例如,数组、字符串等)
我知道GHC应该支持在这两个平台上构建共享库,但我不确定技术细节。导出东西的语法是什么,调用方是否需要做一些特殊的事情来首先初始化DLL
具体来说:假设存在一个函数foobar::FilePath->ioint32
。有没有人能拼凑一个小草图来展示:
- 我需要写些什么Haskell声明来向外界公开这一点
- 如何告诉GHC构建一个独立的DLL/SO文件
- 除了绑定
本身的常规过程之外,调用方需要执行的任何特殊操作foobar
另外,我确实简要地看了一下hs dotnet,但这似乎是Windows特有的。(也就是说,不能在Mono上工作,所以不能在Linux上工作。)就这两种语言而言,您基本上可以假装正在尝试与C代码进行接口 这是一个复杂的主题,因此,我将重点介绍一个简单的示例,您可以在使用下面链接的资源的基础上进行构建
Foreign.C.*
模块中的类型,而不是通常的Haskell类型CInt
而不是Int
,CString
而不是String
,等等。这是最复杂的步骤,尤其是当您必须处理用户定义的类型时
您还必须使用ForeignFunctionInterface
扩展为这些函数编写foreignexport
声明
{-#语言外来函数接口#-}
模块Foo在哪里
输入外文字符串
导入外国C.C.类型
对外出口信用证
foo::CString->IO CInt
foo::CString->IO CInt
foo c_str=do
str作为参考,我可以让下面的程序在Windows下工作
{-# LANGUAGE ForeignFunctionInterface #-}
module Fibonacci () where
import Data.Word
import Foreign.C.Types
fibs :: [Word32]
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
fibonacci :: Word8 -> Word32
fibonacci n =
if n > 47
then 0
else fibs !! (fromIntegral n)
c_fibonacci :: CUChar -> CUInt
c_fibonacci (CUChar n) = CUInt (fibonacci n)
foreign export ccall c_fibonacci :: CUChar -> CUInt
用
ghc --make -shared Fibonacci.hs
这将生成六个文件,其中一个是HSdll.dll
。然后我将其复制到Visual Studio C#项目中,并执行了以下操作:
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
public sealed class Fibonacci : IDisposable
{
#region DLL imports
[DllImport("HSdll.dll", CallingConvention=CallingConvention.Cdecl)]
private static extern unsafe void hs_init(IntPtr argc, IntPtr argv);
[DllImport("HSdll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern unsafe void hs_exit();
[DllImport("HSdll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt32 c_fibonacci(byte i);
#endregion
#region Public interface
public Fibonacci()
{
Console.WriteLine("Initialising DLL...");
unsafe { hs_init(IntPtr.Zero, IntPtr.Zero); }
}
public void Dispose()
{
Console.WriteLine("Shutting down DLL...");
unsafe { hs_exit(); }
}
public UInt32 fibonacci(byte i)
{
Console.WriteLine(string.Format("Calling c_fibonacci({0})...", i));
var result = c_fibonacci(i);
Console.WriteLine(string.Format("Result = {0}", result));
return result;
}
#endregion
}
}
Console.WriteLine()
调用显然是可选的
我还没有试过在Mono/Linux下运行它,但它大概是类似的
综上所述,与C++的DLL工作差不多困难。(即,使类型签名匹配并使封送正确工作是困难的一点。)
我还必须编辑项目设置并选择“允许不安全代码”。对于FFI绑定,您将始终有一个计划B,即“用C编写精简包装”。大多数具有任何类型FFI的语言都可以与C进行互操作。指针:GHC用户指南中的第4.13章和第8.2章,GHC似乎有关于DLL创建的章节:在GHC的最新版本中,这一部分似乎也发生了变化。(!)注意如何编译和链接c/c++代码()。我不知道这个效果是否暴露在.NET/托管代码上。@ Jonke,我们有七个不同的链接,将C++连接到C,原因完全相同。我不知道这是什么东西,但很明显,把它弄对是至关重要的……这看起来几乎就是我想要的。然而#1您是否也需要调用hs\u exit()
#2当我运行此命令时,我在hs_init(null,null)
中得到第二个参数的MarshalDirectiveException。吴?@MathematicalArchid:#1:是的#2:嗯,上面的代码在我的机器上运行,但我一直在想如何正确地封送它。出于某种原因,hs_init
采用了char**argv[]
,尽管据我所知,char*argv[]
就足够了。从我所能找到的示例来看,StringBuilder
的数组在后一种情况下应该可以工作,但我不知道如何处理额外的间接层次(至少在没有手动操作的情况下是这样)。在任何情况下,只要您只是要传递null
,任何指针类型都应该工作。修复了它。我将这两个参数都更改为IntPtr
,但随后出现了不平衡堆栈警告。显然,出于某种原因,我需要添加CallingConvention=CallingConvention.Cdecl
。。。现在它似乎工作得很好。在编译Haskell部分时,我遇到了一个错误:“未定义对‘WinMain’的引用”。这可以通过更改“-optl”->“-optl mwindows”来解决。我从中得到了这个解决方案:不安全的
位只有在我使用原始指针时才需要。有了IntPtr
,它应该可以在没有它们的情况下工作。是的,这都是真的。更改为IntPtr
,删除所有unsafe
关键字,并关闭编译器选项,它仍然可以正常编译和运行。