就这两种语言而言,你基本上可以假装自己在尝试与C代码进行交互。
这是一个复杂的主题,所以我不会尝试解释所有内容,而是专注于提供一个简单的示例,您可以使用下面链接的资源进行构建。
首先,您需要编写包装器(wrapper)来使用Foreign.C.*
模块中的类型,而不是通常的Haskell类型。例如用CInt
代替Int
,用CString
代替String
等。这是最复杂的步骤,特别是当您必须处理用户定义的类型时。
您还必须使用ForeignFunctionInterface
扩展编写foreign export
声明来导出这些函数。
{-# 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)
然后,在编译时,您告诉 GHC 制作一个共享库:
$ ghc -O2 --make -no-hs-main -optl '-shared' -o Foo.so Foo.hs
从C#方面来看,除了导入要调用的函数之外,您还必须导入 hs_init()
并在调用任何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();
}
}
}
}
现在我们进行编译和运行:
$ mcs -unsafe Foo.cs
$ LD_LIBRARY_PATH=. mono Foo.exe
Initializing runtime...
Calling to Haskell...
Hello, C#
Got result: 44
Exiting runtime...
它有效!
资源: