【问题标题】:Calling Haskell from C++ code从 C++ 代码调用 Haskell
【发布时间】:2011-04-21 00:50:42
【问题描述】:

我目前正在用 C++ 编写一个应用程序,发现它的某些功能最好用 Haskell 编写。我在calling Haskell from C code 上看到过说明,但是否可以用 C++ 做同样的事情?

编辑:澄清一下,我正在寻找一种将 Haskell 代码编译成外部库的方法,g++ 可以与 C++ 中的目标代码链接。

更新:我在下面为其他感兴趣的人提供了一个工作示例(也是为了不会忘记)。

【问题讨论】:

  • 你试过了吗?也就是说,当你从 C++ 代码中遵循 C 语言方向时,它是否有效?除非准备这些说明的人忘记了 extern "C",严重依赖 void 指针的隐式转换,或者命名了某个变量 class 等,否则我不明白为什么不这样做。
  • 因为按照说明,我得用ghc编译C代码。
  • 使用g++ 链接的complete example in wiki.haskell 不再起作用。 The new method 似乎依赖 ghc 进行所有编译和链接。在 ArchLinux 上:ghc --make -dynamic -no-hs-main test.cpp Foo.hs -lstdc++ -o test

标签: c++ haskell linker ffi


【解决方案1】:

对于任何感兴趣的人,这是我终于开始工作的测试用例:


M.hs

module Foo where

foreign export ccall foo :: Int -> Int

foo :: Int -> Int
foo = floor . sqrt . fromIntegral

test.cpp

#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;
}

我在我的 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

【讨论】:

  • 我也让它在 Linux 上工作。但是,我还发现你可以通过链接 GHC 而不是 G++ 来大大简化最后一步(因为手动链接步骤对于 C++ 来说要简单得多)。具体可以链接:ghc -no-hs-main M.o test.o -lstdc++
  • 另外,对于那些在 linux 上通过 apt-get ghc 安装了 haskell 的用户,应该可以使用 -I/usr/lib/ghc/include 获得相应的包含目录。如果您的 c++ 程序仍然找不到 HsFFI.h,请尝试在系统上的其他地方搜索 HsFFI.h,可能使用 findlocate
  • 同意@Onyxite 的提示。作为对 Haskell 粉丝俱乐部的致敬,我在下面制作了一段关于它的视频:asciinema.org/a/jl6B8b327H8knPo9gMWEupWkB
【解决方案2】:

编辑:您还应该在下面看到 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 的回答,因为他就是这样做的。)

【讨论】:

  • 感谢您的详细回答。按照您的指示,我得到“仅” 1 个未定义的引用 (for std::__ostream_insert) ,所以这当然是一个改进。我想现在只需要协调所有必要的库,我将留到明天。 -v 和 -l 选项确实适用于 GHC。唯一的问题是第一个 ghc 命令需要标志 -XForeignFunctionInterface(还有一些包含带有 -I 标志的目录)
  • @Tomer:或者(到命令行上的 -XForeignFunctionInterface)您可以在任何使用 FFI 的 haskell 源的顶部放置一个语言编译指示:{-# LANGUAGE ForeignFunctionInterface #-}
  • 由于某种原因 A_stub.o 没有为我编译,我在 linux 上,然后一切都编译正常
  • @pyCthon:您是否使用了 Tomer 在编译“A.hs”程序时提到的“-XForeignFunctionInterface”选项? (我刚刚编辑了答案以添加它;我之前省略了它。)
  • 你也可以把这个{-# LANGUAGE ForeignFunctionInterface #-}放在haskell代码之上。然后,您不再需要使用-XForeignFunctionInterface 选项。
【解决方案3】:

这是一个关于该主题的教程:

https://github.com/jarrett/cpphs

它涵盖了从 C++ 调用 Haskell 和从 Haskell 调用 C。

【讨论】:

    【解决方案4】:

    既然您可以从 C 中调用 Haskell,那么您没有理由不能从 C++ 中调用它。另一方面,从 Haskell 调用 C++ 要困难得多,而且通常需要 C 包装器。

    编辑以展开。说明错误到不完整。它们是一个维基页面。直接看GHC手册:http://www.haskell.org/ghc/docs/6.12.2/html/users_guide/ffi-ghc.html

    这描述了如何导出函数,以及如何使用你自己的 main。注意它在哪里说“其他语言,比如 C”。之所以这么说是因为您可以使用任何可以调用您正在导出的普通 C 函数以及 HsFFI.h 提供的语言(和编译器)来执行此操作。这与语言无关,与编译器无关。它所需要的只是能够使用系统上的标准调用约定调用 C 函数,而 C++ 编译器(例如 g++)当然提供了这一点。

    【讨论】:

    • 按照说明,我得用ghc编译C代码。一种适用于 C++ 的解决方案是最终使用 gcc/g++ 编译代码
    • @Tomer Vromen:见我上面的编辑。您想要的东西开箱即用,并直接记录在 GHC 手册中。
    • 也澄清了一点——你可以用 gcc/g++ 编译 C++ 代码,用 GHC 编译 Haskell 代码。
    • 对不起,我太密集了,但我只是收到一堆链接器错误。我尝试链接 ghc 生成的 .o 文件,以及 GHC 库路径中的各种库。我总是收到一条消息,说 hs_init 是一个未定义的引用。在某处有一个可行的分步示例吗?
    • 托默 - 没问题!在我发表评论后,我才意识到这将是一个问题。希望我的完整回答能澄清这一点。 (不幸的是,我不知道任何分步示例,但是,此时我只是 C++ 用户。)
    【解决方案5】:

    cabal 2.0 添加了“foreign-library”功能,这似乎解决了链接器问题,并且总体上使整个构建过程更加愉快。

    我整理了一个简短的示例教程https://github.com/pdlla/haskell-ffi-cabal-foreign-library-examples

    【讨论】:

      猜你喜欢
      • 2017-04-10
      • 1970-01-01
      • 2013-05-13
      • 2010-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-22
      • 1970-01-01
      相关资源
      最近更新 更多