我目前正在用C ++编写一个应用程序,发现它的一些功能可以更好地用Haskell编写。我已经看到了calling Haskell from C code的说明,但是可以用C ++做同样的事吗?
编辑:为了澄清,我正在寻找的是一种将Haskell代码编译成外部库的方法,g ++可以与C ++中的目标代码链接。
更新:我在下面为其他感兴趣的人提供了一个工作示例(也让我不会忘记)。
答案 0 :(得分:56)
对任何有兴趣的人来说,这是我终于开始工作的测试案例:
module Foo where
foreign export ccall foo :: Int -> Int
foo :: Int -> Int
foo = floor . sqrt . fromIntegral
#include <iostream>
#include "M_stub.h"
int main(int argc, char *argv[])
{
std::cout << "hello\n";
hs_init(&argc, &argv);
std::cout << foo(500) << "\n";
hs_exit();
return 0;
}
我做了编译&amp;链接在我的Windows机器上。要运行的命令(按此顺序)是:
>ghc -XForeignFunctionInterface -c M.hs
>g++ -c test.cpp -I"c:\Program Files\Haskell Platform\2010.2.0.0\lib\include"
>g++ -o test.exe -DDONT_WANT_WIN32_DLL_SUPPORT M.o M_stub.o test.o -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib\haskell98-1.0.1.1" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib\random-1.0.0.2" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib\time-1.1.4" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib\process-1.0.1.3" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib\directory-1.0.1.1" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib\old-time-1.0.0.5" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib\old-locale-1.0.0.2" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib\filepath-1.1.0.4" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib\Win32-2.2.0.2" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib\bytestring-0.9.1.7" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib\array-0.3.0.1" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib\base-4.2.0.2" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib\integer-gmp-0.2.0.1" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib\ghc-prim-0.2.0.0" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib" -L"C:\Program Files\Haskell Platform\2010.2.0.0\lib/gcc-lib" -lHSrtsmain -lHShaskell98-1.0.1.1 -lHSrandom-1.0.0.2 -lHStime-1.1.4 -lHSprocess-1.0.1.3 -lHSdirectory-1.0.1.1 -lHSold-time-1.0.0.5 -lHSold-locale-1.0.0.2 -lHSfilepath-1.1.0.4 -lHSWin32-2.2.0.2 -luser32 -lgdi32 -lwinmm -ladvapi32 -lshell32 -lshfolder -lHSbytestring-0.9.1.7 -lHSarray-0.3.0.1 -lHSbase-4.2.0.2 -lwsock32 -luser32 -lshell32 -lHSinteger-gmp-0.2.0.1 -lHSghc-prim-0.2.0.0 -lHSrts -lm -lwsock32 -u _ghczmprim_GHCziTypes_Izh_static_info -u _ghczmprim_GHCziTypes_Czh_static_info -u _ghczmprim_GHCziTypes_Fzh_static_info -u _ghczmprim_GHCziTypes_Dzh_static_info -u _base_GHCziPtr_Ptr_static_info -u _base_GHCziWord_Wzh_static_info -u _base_GHCziInt_I8zh_static_info -u _base_GHCziInt_I16zh_static_info -u _base_GHCziInt_I32zh_static_info -u _base_GHCziInt_I64zh_static_info -u _base_GHCziWord_W8zh_static_info -u _base_GHCziWord_W16zh_static_info -u _base_GHCziWord_W32zh_static_info -u _base_GHCziWord_W64zh_static_info -u _base_GHCziStable_StablePtr_static_info -u _ghczmprim_GHCziTypes_Izh_con_info -u _ghczmprim_GHCziTypes_Czh_con_info -u _ghczmprim_GHCziTypes_Fzh_con_info -u _ghczmprim_GHCziTypes_Dzh_con_info -u _base_GHCziPtr_Ptr_con_info -u _base_GHCziPtr_FunPtr_con_info -u _base_GHCziStable_StablePtr_con_info -u _ghczmprim_GHCziBool_False_closure -u _ghczmprim_GHCziBool_True_closure -u _base_GHCziPack_unpackCString_closure -u _base_GHCziIOziException_stackOverflow_closure -u _base_GHCziIOziException_heapOverflow_closure -u _base_ControlziExceptionziBase_nonTermination_closure -u _base_GHCziIOziException_blockedIndefinitelyOnMVar_closure -u _base_GHCziIOziException_blockedIndefinitelyOnSTM_closure -u _base_ControlziExceptionziBase_nestedAtomically_closure -u _base_GHCziWeak_runFinalizzerBatch_closure -u _base_GHCziTopHandler_runIO_closure -u _base_GHCziTopHandler_runNonIO_closure -u _base_GHCziConc_ensureIOManagerIsRunning_closure -u _base_GHCziConc_runSparks_closure -u _base_GHCziConc_runHandlers_closure -lHSffi
最后一个g ++命令的长参数列表来自运行
>ghc M.hs -v
然后复制命令“*** Linker:”(需要删除一些第一个参数)。
结果:
>test
hello
22
答案 1 :(得分:31)
编辑:您还应该看到Tomer的答案如下。我在这里的答案描述了正在发生的事情的理论,但我可能有一些执行细节不完整,而他的答案是一个完整的工作实例。
正如sclv所示,编译应该没问题。可能存在连接C ++代码的困难,在这里你将获得链接所有需要的运行时库的一些困难。问题是Haskell程序需要与Haskell运行时库和C ++链接程序需要与C ++运行时库链接。在您参考的Wiki页面中,当它们执行时
$ ghc -optc -O test.c A.o A_stub.o -o test
编译C程序,它实际上做了两个步骤:它将C程序编译成目标文件,然后将它们链接在一起。写出来,这可能是(可能不太正确,因为我不会说GHC):
$ ghc -c -optc-O test.c -o test.o
$ ghc test.o A.o A_stub.o -o test
GHC在编译C程序时就像GCC(和IIUC,功能是 GCC)。但是,当链接它时,它与直接调用GCC时会发生的情况不同,因为它还神奇地包含了Haskell运行时库。 G ++对C ++程序的工作方式相同 - 当它用作链接器时,它包含C ++运行时库。
因此,正如我所提到的,您需要以与两个运行时库链接的方式进行编译。如果以详细模式运行G ++来编译和链接程序,如下所示:
$ g++ test.cpp -o test -v
它将创建一个关于它正在做什么的长输出列表;最后将是一行输出,它在其中进行链接(使用collect2
子程序)指示它链接到哪些库。您可以将其与输出进行比较,以编译简单的C程序,以查看C ++的不同之处;在我的系统上,它添加了-lstdc++
。
因此,您应该能够编译和链接混合的Haskell / C ++程序,如下所示:
$ ghc -c -XForeignFunctionInterface -O A.hs # compile Haskell object file.
$ g++ -c -O test.cpp # compile C++ object file.
$ ghc A.o A_stub.o test.o -lstdc++ -o test # link
在那里,因为你已经指定了-lstdc++
,它将包含C ++运行时库(假设-l
是正确的GHC语法;你需要检查),因为你已经链接了使用ghc
,它将包含Haskell运行时库。这应该会产生一个工作计划。
或者,您应该可以使用GHC执行类似于-v
输出调查的操作,并找出它链接到Haskell支持的Haskell运行时库(或库),然后在链接时添加该库您的程序使用C ++,就像您已经为纯C ++程序所做的那样。 (有关详细信息,请参阅Tomer的答案,因为这就是他所做的。)
答案 2 :(得分:9)
答案 3 :(得分:2)
由于你可以从C调用Haskell,所以没有理由不能用C ++调用它。另一方面,从Haskell调用C ++要困难得多,通常需要一个C包装器。
编辑以展开。说明不完整是错误的。他们是一个维基页面。直接查看GHC手册:http://www.haskell.org/ghc/docs/6.12.2/html/users_guide/ffi-ghc.html
介绍如何导出函数以及如何使用自己的main函数。注意它在哪里说“其他语言,比如说C.”它说这是因为你可以从任何语言(和编译器)执行此操作,这些语言可以调用您正在导出的vanilla C函数,并且HsFFI.h提供了这些函数。这与语言无关,与编译器无关。它需要的是能够使用系统上的标准调用约定调用C函数,C ++编译器(例如g ++)当然提供这些约定。
答案 4 :(得分:0)
cabal 2.0添加了“外部库”功能,该功能似乎可以解决链接程序问题,并且通常使整个构建过程更加轻松。
我整理了一个简短的示例教程https://github.com/pdlla/haskell-ffi-cabal-foreign-library-examples