从C#调用Haskell [英] Calling Haskell from C#

查看:93
本文介绍了从C#调用Haskell的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚花了最后一周的时间来了解如何从C#执行C ++代码作为我日常工作的一部分。它花了我们一直弄清楚,但最终的解决方案是相当简单的。



现在我很好奇......从C#调用Haskell有多难? ? (请注意:这是从C#中调用Haskell ,而不是其他方式,所以主要的可执行文件是C#。)



如果真的很难,我不会打扰。但是,如果它相当容易,我可能不得不去玩它。



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

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



具体来说:假设存在一个函数 foobar :: FilePath - > IO Int32 。有人可以将一个小图表示出来:


  • 我需要写些什么Haskell声明才能将它暴露给外部世界。

  • 如何告诉GHC构建一个自包含的DLL / SO文件

  • 除了通常的绑定过程之外,调用者需要做的任何特殊操作 foobar 本身。



我并不太担心C#端;我想我已经或多或少感到困惑了。



我简单地看过 hs-dotnet ,但这似乎是Windows特有的。 (即,不会与Mono一起工作,所以在Linux上不起作用。)

解决方案

就两种语言而言有关的,你可以基本假装你试图与C代码接口。



这是一个复杂的话题,所以不是试图解释所有这些,我会集中精力您可以使用下面链接的资源构建一个简单的示例。


  1. 首先,您需要为您编写包装Haskell函数使用来自 Foreign.C。* 模块的类型,而不是通常的haskell类型。 CInt 而不是 Int CString 而不是 String 等等,这是最复杂的一步,尤其是当你必须处理用户定义的类型时。



    你也必须为使用 ForeignFunctionInterface 扩展名的函数编写外部出口声明。

      { - #LANGUAGE ForeignFunctionInterface# - } 
    module Foo where

    import Foreign.C.String
    import Foreign.C.Types

    国外出口ccall
    foo :: CString - > IO CInt

    foo :: CString - > IO CInt
    foo c_str = do
    str < - peekCString c_str
    结果< - hs_foo str
    返回$ fromIntegral结果

    hs_foo :: String - > IO Int
    hs_foo str = do
    putStrLn $Hello,++ str
    return(length str + 42)


     prettyprint-override>  $ ghc -O2 --make -no-hs-main -optl'-shared'-o Foo.so Foo.hs 


  2. 在C#方面,除了导入要调用的函数之外,还必须导入 hs_init() 并调用它来初始化运行时系统,然后才能调用任何Haskell函数。您完成后还应该调用 hs_exit()

     使用System; 
    使用System.Runtime.InteropServices;

    namespace Foo {
    class MainClass {
    [DllImport(Foo.so,CallingConvention = CallingConvention.Cdecl)]
    private static extern void hs_init(IntPtr argc ,IntPtr argv);

    [DllImport(Foo.so,CallingConvention = CallingConvention.Cdecl)]
    private static extern void hs_exit();

    [DllImport(Foo.so,CallingConvention = CallingConvention.Cdecl)]
    private static extern int foo(string str);

    public static void Main(string [] args){
    Console.WriteLine(Initializing runtime ...);
    hs_init(IntPtr.Zero,IntPtr.Zero);

    尝试{
    Console.WriteLine(Calling to Haskell ...);
    int result = foo(C#);
    Console.WriteLine(有结果:{0},result);
    } finally {
    Console.WriteLine(Exiting runtime ...);
    hs_exit();
    }
    }
    }
    }


  3. <现在我们编译并运行:

      $ mcs -unsafe Foo.cs 
    $ LD_LIBRARY_PATH =。 mono Foo.exe
    初始化运行时...
    调用Haskell ...
    您好,C#
    有结果:44
    退出运行时...

    有效!


资源:


I just spent the last week or so figuring out how to execute C++ code from C# as part of my day job. It took us forever to figure it out, but the final solution is fairly simple.

Now I'm curious... How hard would it be to call Haskell from C#? (Note carefully: That's call Haskell from C#, not the other way around. So the main executable is C#.)

If it's really hard, I won't bother. But if it's reasonably easy, I might have to have a play with it...

Basically, we wrote some C++ code. On Windows it gets compiled into a DLL, on Linux it gets compiled into a shared object (*.so). Then on the C# side you do a DllImport and write some manual memory management code if you're trying to pass anything nontrivial across. (E.g., arrays, strings, etc.)

I know GHC is supposed to support building shared libraries on both platforms, but I'm not sure of the technical details. What's the syntax for exporting stuff, and does the caller have to do anything special to initialise the DLL first?

To be concrete: Suppose there exists a function foobar :: FilePath -> IO Int32. Can somebody throw together a small sketch showing:

  • What Haskell declarations I need to write to expose this to the outside world.
  • How do I tell GHC to build a single self-contained DLL / SO file.
  • Anything special the caller needs to do, beyond the usual process of binding foobar itself.

I'm not too worried about the actual syntax for the C# side; I think I've more or less puzzled that out.

P.S. I did briefly look at hs-dotnet, but this appears to be Windows-specific. (I.e., won't work with Mono, so won't work on Linux.)

解决方案

As far as both languages are concerned, you can basically pretend you're trying to interface with C code.

This is a complex topic, so rather than try to explain all of it, I will focus on making a simple example that you can build on using the resources linked below.

  1. First, you need to write wrappers for your Haskell functions that use types from the Foreign.C.* modules instead of the usual haskell types. CInt instead of Int, CString instead of String, etc. This is the most complicated step, especially when you have to deal with user-defined types.

    You also have to write foreign export declarations for those functions using the ForeignFunctionInterface extension.

    {-# LANGUAGE ForeignFunctionInterface #-}
    module Foo where
    
    import Foreign.C.String
    import Foreign.C.Types
    
    foreign export ccall
      foo :: CString -> IO CInt
    
    foo :: CString -> IO CInt
    foo c_str = do
      str    <- peekCString c_str
      result <- hs_foo str 
     return $ fromIntegral result
    
    hs_foo :: String -> IO Int
    hs_foo str = do
      putStrLn $ "Hello, " ++ str
      return (length str + 42)
    

  2. Then, when compiling, you tell GHC to make a shared library:

    $ ghc -O2 --make -no-hs-main -optl '-shared' -o Foo.so Foo.hs
    

  3. From the C# side, in addition to importing the function you want to call, you also have to import hs_init() and call it to initialize the runtime system before you can call any Haskell functions. You should also call hs_exit() when you're done.

    using System;
    using System.Runtime.InteropServices;
    
    namespace Foo {
        class MainClass {
            [DllImport("Foo.so", CallingConvention = CallingConvention.Cdecl)]
            private static extern void hs_init(IntPtr argc, IntPtr argv);
    
            [DllImport("Foo.so", CallingConvention = CallingConvention.Cdecl)]
            private static extern void hs_exit();
    
            [DllImport("Foo.so", CallingConvention = CallingConvention.Cdecl)]
            private static extern int foo(string str);
    
            public static void Main(string[] args) {
                Console.WriteLine("Initializing runtime...");
                hs_init(IntPtr.Zero, IntPtr.Zero);
    
                try {
                    Console.WriteLine("Calling to Haskell...");
                    int result = foo("C#");
                    Console.WriteLine("Got result: {0}", result);
                } finally {
                    Console.WriteLine("Exiting runtime...");
                    hs_exit();
                }
            }
        }
    }
    

  4. Now we compile and run:

    $ mcs -unsafe Foo.cs
    $ LD_LIBRARY_PATH=. mono Foo.exe
    Initializing runtime...
    Calling to Haskell...
    Hello, C#
    Got result: 44
    Exiting runtime...
    

    It works!

Resources:

这篇关于从C#调用Haskell的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆