私は現在C++でアプリを作成しており、その機能の一部はHaskellで作成した方がよいことがわかりました。 CコードからHaskellを呼び出す の説明を見てきましたが、C++でも同じことを実行できますか?
EDIT:明確にするために、私が探しているのは、g ++がC++のオブジェクトコードとリンクできる外部ライブラリにHaskellコードをコンパイルする方法です。
UPDATE:興味のある他の人のために、以下の作業例を用意しました(これも忘れないように)。
編集:以下のTomerの回答もご覧ください。ここでの私の答えは何が起こっているかの理論を説明していますが、実行の詳細の一部は不完全かもしれませんが、彼の答えは完全に機能する例です。
Sclvが示すように、コンパイルは問題ありません。 C++コードをリンクすることは困難である可能性が高く、必要なすべてのランタイムライブラリをリンクするのに少し困難があります。問題は、HaskellプログラムをHaskellランタイムライブラリとリンクする必要があること、およびC++プログラムは、C++ランタイムライブラリとリンクする必要があります。あなたが参照するWikiページで、彼らがそうするとき
$ ghc -optc -O test.c A.o A_stub.o -o test
cプログラムをコンパイルするには、実際には2つのステップを実行します。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、機能的にisGCC)のように機能します。ただし、リンクする場合は、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ランタイムライブラリ(1つまたは複数)を特定し、プログラムをリンクするときにそのライブラリを追加する必要があります。純粋なC++プログラムですでに行っているように、C++。 (詳細については、Tomerの回答を参照してください。Tomerの回答です。)
興味のある方には、これが私がようやく機能したテストケースです。
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;
}
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
CからHaskellを呼び出すことができるので、C++から呼び出せない理由はありません。一方、HaskellからのC++の呼び出しははるかに難しく、通常はCラッパーが必要です。
編集して展開します。指示が不完全であることは間違いです。彼らはwikiページです。 GHCマニュアルを直接見てください: http://www.haskell.org/ghc/docs/6.12.2/html/users_guide/ffi-ghc.html
ここでは、関数をエクスポートする方法と、独自のメインを使用する方法について説明します。 「他の言語、Cと言う」と書かれている箇所に注意してください。これは、エクスポートするVanilla C関数を呼び出すことができ、HsFFI.hが提供する任意の言語(およびコンパイラ)からこれを実行できるためです。これは言語に依存せず、コンパイラに依存しません。必要なのは、C++コンパイラ(g ++など)が確実に提供する、システムの標準の呼び出し規約を使用してC関数を呼び出す機能だけです。
cabal 2.0は、「外部ライブラリ」機能を追加しました。これは、リンカーの問題を解決するようであるだけでなく、ビルドプロセス全体を一般的にはるかに快適にします。
私は短いサンプルチュートリアルをまとめました https://github.com/pdlla/haskell-ffi-cabal-foreign-library-examples