从C#调用Haskell [英] Calling Haskell from C#
问题描述
我刚刚花了最后一周的时间来了解如何从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代码接口。
这是一个复杂的话题,所以不是试图解释所有这些,我会集中精力您可以使用下面链接的资源构建一个简单的示例。
-
首先,您需要为您编写包装Haskell函数使用来自
Foreign.C。*
模块的类型,而不是通常的haskell类型。CInt
而不是Int
,CString
而不是String
等等,这是最复杂的一步,尤其是当你必须处理用户定义的类型时。
你也必须为使用
ForeignFunctionInterface
扩展名的函数编写外部出口
声明。{ - #LANGUAGE ForeignFunctionInterface# - }
$ p $然后,在编译时,你告诉GHC创建一个共享库:
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
-
在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();
}
}
}
}
- <现在我们编译并运行:
$ 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.
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 ofInt
,CString
instead ofString
, 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 theForeignFunctionInterface
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)
Then, when compiling, you tell GHC to make a shared library:
$ ghc -O2 --make -no-hs-main -optl '-shared' -o Foo.so Foo.hs
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 callhs_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(); } } } }
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屋!