43

私は先週ほど、日常業務の一環として C# から C++ コードを実行する方法を考え出しました。それを理解するのに永遠にかかりましたが、最終的な解決策はかなり単純です.

今、私は興味があります... C# から Haskell を呼び出すのはどれほど難しいでしょうか? (注意してください: これは C#からHaskell を呼び出すことであり、その逆ではありません。したがって、メインの実行可能ファイルは C# です。)

それが本当に難しいなら、私は気にしません。しかし、それがかなり簡単なら、私はそれで遊ぶ必要があるかもしれません...

基本的に、いくつかの C++ コードを作成しました。Windows では DLL にコンパイルされ、Linux では共有オブジェクト ( *.so) にコンパイルされます。次に、C# 側で、DllImport自明でないものを渡そうとする場合は、手動のメモリ管理コードを作成します。(例: 配列、文字列など)

GHC が両方のプラットフォームで共有ライブラリの構築をサポートすることになっていることは知っていますが、技術的な詳細についてはよくわかりません。ものをエクスポートするための構文は何ですか?呼び出し元は最初に DLL を初期化するために何か特別なことをする必要がありますか?

具体的に言うと、 function が存在するとしますfoobar :: FilePath -> IO Int32。誰かが以下を示す小さなスケッチをまとめてくれませんか?

  • これを外の世界に公開するために、どの Haskell 宣言を書く必要があるか。
  • 単一の自己完結型 DLL / SO ファイルをビルドするように GHC に指示するにはどうすればよいですか?
  • 自身をバインドする通常のプロセスを超えて、呼び出し元が行う必要がある特別なfoobarこと。

C# 側の実際の構文についてはあまり心配していません。私は多かれ少なかれそれを困惑させたと思います。

PS私は簡単に見ましたhs-dotnetが、これはWindows固有のようです。(つまり、Mono では動作しないため、Linux でも動作しません。)

4

2 に答える 2

53

両方の言語に関する限り、基本的には C コードとやり取りしようとしているふりをすることができます。

これは複雑なトピックなので、すべてを説明しようとするのではなく、以下にリンクされているリソースを使用して構築できる簡単な例を作成することに焦点を当てます.

  1. Foreign.C.*まず、通常の haskell 型の代わりにモジュールの型を使用する Haskell 関数のラッパーを作成する必要があります。CIntの代わりにInt、 のCString代わりにStringなど。これは、特にユーザー定義型を処理する必要がある場合に、最も複雑な手順です。

    また、拡張foreign export機能を使用してこれらの関数の宣言を記述する必要があります。ForeignFunctionInterface

    {-# 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. 次に、コンパイル時にGHCに共有ライブラリを作成するように指示します:

    $ ghc -O2 --make -no-hs-main -optl '-shared' -o Foo.so Foo.hs
    
  3. hs_init()C# 側からは、呼び出す関数をインポートするだけでなく、Haskell 関数を呼び出す前に、それをインポートして呼び出してランタイム システムを初期化する必要があります。また、終わったら電話hs_exit()する必要があります。

    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. 次に、コンパイルして実行します。

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

    できます!

資力:

于 2013-05-17T21:15:08.460 に答える