Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从C调用Haskell# 我刚刚花了一个星期左右的时间来计算如何从C++中执行C++代码作为我日常工作的一部分。我们花了很长时间才弄明白,但最终的解决方案相当简单_C#_Haskell_Ffi - Fatal编程技术网

C# 从C调用Haskell# 我刚刚花了一个星期左右的时间来计算如何从C++中执行C++代码作为我日常工作的一部分。我们花了很长时间才弄明白,但最终的解决方案相当简单

C# 从C调用Haskell# 我刚刚花了一个星期左右的时间来计算如何从C++中执行C++代码作为我日常工作的一部分。我们花了很长时间才弄明白,但最终的解决方案相当简单,c#,haskell,ffi,C#,Haskell,Ffi,现在我很好奇。。。从C#给Haskell打电话有多难?(请注意:这是从C#调用Haskell,而不是相反。因此,主可执行文件是C#。) 如果真的很难,我就不麻烦了。但如果它相当容易,我可能不得不玩它 基本上,我们编写了一些C++代码。在Windows上它被编译成DLL,在Linux上它被编译成共享对象(*.so)。然后在C#端执行一个DllImport,并编写一些手动内存管理代码,如果您试图传递任何不寻常的内容。(例如,数组、字符串等) 我知道GHC应该支持在这两个平台上构建共享库,但我不确定技

现在我很好奇。。。从C#给Haskell打电话有多难?(请注意:这是从C#调用Haskell,而不是相反。因此,主可执行文件是C#。)

如果真的很难,我就不麻烦了。但如果它相当容易,我可能不得不玩它

基本上,我们编写了一些C++代码。在Windows上它被编译成DLL,在Linux上它被编译成共享对象(
*.so
)。然后在C#端执行一个
DllImport
,并编写一些手动内存管理代码,如果您试图传递任何不寻常的内容。(例如,数组、字符串等)

我知道GHC应该支持在这两个平台上构建共享库,但我不确定技术细节。导出东西的语法是什么,调用方是否需要做一些特殊的事情来首先初始化DLL

具体来说:假设存在一个函数
foobar::FilePath->ioint32
。有没有人能拼凑一个小草图来展示:

  • 我需要写些什么Haskell声明来向外界公开这一点
  • 如何告诉GHC构建一个独立的DLL/SO文件
  • 除了绑定
    foobar
    本身的常规过程之外,调用方需要执行的任何特殊操作
我不太担心C端的实际语法;我想我或多或少已经弄明白了


另外,我确实简要地看了一下hs dotnet,但这似乎是Windows特有的。(也就是说,不能在Mono上工作,所以不能在Linux上工作。)

就这两种语言而言,您基本上可以假装正在尝试与C代码进行接口

这是一个复杂的主题,因此,我将重点介绍一个简单的示例,您可以在使用下面链接的资源的基础上进行构建

  • 首先,您需要为Haskell函数编写包装器,这些函数使用
    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
    关键字,并关闭编译器选项,它仍然可以正常编译和运行。