从 C++ 调用 Haskell代码

发布于 2024-09-25 18:04:40 字数 325 浏览 8 评论 0原文

我目前正在用 C++ 编写一个应用程序,发现它的一些功能用 Haskell 编写会更好。我看过从 C 代码调用 Haskell 的说明,但是是否可以用C++?

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

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

I'm currently writing an app in C++ and found that some of its functionality would be better written in Haskell. I've seen instructions on calling Haskell from C code, but is it possible to do the same with C++?

EDIT: To clarify, what I'm looking for is a way to compile Haskell code into an external library that g++ can link with the object code from C++.

UPDATE: I've put up a working example below for anyone else interested (also so I won't forget).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

蓝眼泪 2024-10-02 18:04:40

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


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

,然后将命令复制到显示“***链接器:”的位置(需要删除一些第一个参数) 。


结果:

>test
hello
22

To anyone interested, this is the test case that I've finally got working:


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

I did the compiles & linking on my Windows machine. The commands to run (in this order) are:

>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

The long list of parameters for the last g++ command is from running

>ghc M.hs -v

and then copying the command where it says "***Linker:" (some of the first parameters need to be removed).


The result:

>test
hello
22
阿楠 2024-10-02 18:04:40

编辑:您还应该看到下面托默的回答。我在这里的回答描述了正在发生的事情的理论,但我可能有一些执行的细节不完整,而他的回答是一个完整的工作示例。

正如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

在编译 C 程序时,GHC 的行为就像 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++ 程序所做的那样。 (有关详细信息,请参阅托默的回答,因为他就是这么做的。)

Edit: You should also see Tomer's answer below. My answer here describes the theory of what's going on, but I may have some of the details of execution incomplete, whereas his answer is a complete working example.

As sclv indicates, compiling should be no problem. The difficulty there is likely to be linking the C++ code, and here you will have a little bit of difficulty with getting all the needed runtime libraries linked in. The problem is that Haskell programs need to be linked with the Haskell runtime libraries, and C++ programs need to be linked with the C++ runtime libraries. In the Wiki page you reference, when they do

$ ghc -optc -O test.c A.o A_stub.o -o test

to compile the C program, that actually does two steps: It compiles the C program into an object file, and then links it together. Written out, that would be something like (probably not quite right, as I don't speak GHC):

$ ghc -c -optc-O test.c -o test.o
$ ghc test.o A.o A_stub.o -o test

GHC just acts like GCC (and, IIUC, functionally is GCC) when compiling the C program. When linking it, however, it is different from what happens if you call GCC directly, because it also magically includes the Haskell runtime libraries. G++ works the same way for C++ programs -- when it's used as a linker, it includes the C++ runtime libraries.

So, as I mentioned, you need to compile in a way that links with both runtime libraries. If you run G++ in verbose mode to compile and link a program, like so:

$ g++ test.cpp -o test -v

it will create a long list of output about what it's doing; at the end will be a line of output where it does the linking (with the collect2 subprogram) indicating what libraries it links to. You can compare that to the output for compiling a simple C program to see what's different for C++; on my system, it adds -lstdc++.

Thus, you should be able to compile and link your mixed Haskell/C++ program like so:

$ 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

There, because you've specified -lstdc++, it will include the C++ runtime library (assuming -l is the right GHC syntax; you'll need to check), and because you've linked with ghc, it will include the Haskell runtime library. This should result in a working program.

Alternately, you should be able to do something similar to the -v output investigation with GHC, and figure out what Haskell runtime library (or libraries) it links to for Haskell support, and then add that library when linking your program with C++, just as you already do for pure C++ programs. (See Tomer's answer for details of that, since that's what he did.)

高速公鹿 2024-10-02 18:04:40

这是关于该主题的教程:

https://github.com/jarrett/cpphs

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

This is a tutorial on the topic:

https://github.com/jarrett/cpphs

It covers calling Haskell from C++ and calling C from Haskell.

人事已非 2024-10-02 18:04:40

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

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

cabal 2.0 added the "foreign-library" feature which seems to solve the linker issues as well as making the whole build process much more pleasant in general.

I put together a short example tutorial https://github.com/pdlla/haskell-ffi-cabal-foreign-library-examples

剪不断理还乱 2024-10-02 18:04:40

由于您可以从 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”。它这样说是因为您可以从任何可以调用您正在导出的普通 C 函数以及 HsFFI.h 提供的语言(和编译器)来执行此操作。这与语言和编译器无关。它所需要的只是能够使用系统上的标准调用约定来调用 C 函数,C++ 编译器(例如 g++)当然可以提供这一功能。

Since you can call Haskell from C, there's no reason you can't call it from C++. Calling C++ from Haskell, on the other hand, is much harder and usually requires a C wrapper.

Edit to expand. The instructions are wrong to incomplete. They're a wiki page. Look directly at the GHC manual: http://www.haskell.org/ghc/docs/6.12.2/html/users_guide/ffi-ghc.html

This describes how to export functions, and how to use your own main. Note where it says "some other language, say C." It says this because you can do this from any language (and compiler) that can invoke the vanilla C functions that you're exporting, and that HsFFI.h provides. This is language agnostic, and compiler agnostic. All it requires is the ability to invoke C functions using the standard calling conventions on your system, which a C++ compiler (such as g++) certainly provides.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文