Грубо следуя этому руководству, мне удалось запустить этот игрушечный проект. Он вызывает функцию Haskell из программы C++.
-
Foo.hs
{-# LANGUAGE ForeignFunctionInterface #-} module Foo where foreign export ccall foo :: Int -> Int -> IO Int foo :: Int -> Int -> IO Int foo n m = return . sum $ f n ++ f m f :: Int -> [Int] f 0 = [] f n = n : f (n-1)
-
bar.C++
#include "HsFFI.h" #include FOO // Haskell module (path defined in build script) #include <iostream> int main(int argc, char *argv[]) { hs_init(&argc, &argv); std::cout << foo(37, 19) << "\n"; hs_exit(); return 0; }
-
call-haskell-from-cxx.cabal
name: call-haskell-from-cxx version: 0.1.0.0 build-type: Simple cabal-version: >=1.10 executable foo.so main-is: Foo.hs build-depends: base >=4.10 && <4.11 ghc-options: -shared -fPIC -dynamic extra-libraries: HSrts-ghc8.2.1 default-language: Haskell2010
-
скрипт сборки
#!/bin/bash hs_lib="foo.so" hs_obj="dist/build/$hs_lib/$hs_lib" ghc_version="8.2.1" # May need to be tweaked, ghc_libdir="/usr/local/lib/ghc-$ghc_version" # depending on system setup. set -x cabal build g++ -I "$ghc_libdir/include" -D"FOO=\"${hs_obj}-tmp/Foo_stub.h\"" -c bar.c++ -o test.o g++ test.o "$hs_obj" \ -L "$ghc_libdir/rts" "-lHSrts-ghc$ghc_version" \ -o test env LD_LIBRARY_PATH="dist/build/$hs_lib:$ghc_libdir/rts:$LD_LIBRARY_PATH" \ ./test
Это работает (Ubuntu 16.04, GCC 5.4.0), печать 893
- но это не очень надежный, а именно, если я удалю фактический вызов функции Haskell, то есть std::cout << foo(37, 19) << "\n";
line, то он не работает на этапе связывания с сообщением об ошибке
/usr/local/lib/ghc-8.2.1/rts/libHSrts-ghc8.2.1.so: undefined reference to 'base_GHCziTopHandler_flushStdHandles_closure'
/usr/local/lib/ghc-8.2.1/rts/libHSrts-ghc8.2.1.so: undefined reference to 'base_GHCziStable_StablePtr_con_info'
/usr/local/lib/ghc-8.2.1/rts/libHSrts-ghc8.2.1.so: undefined reference to 'base_GHCziPtr_FunPtr_con_info'
/usr/local/lib/ghc-8.2.1/rts/libHSrts-ghc8.2.1.so: undefined reference to 'base_GHCziWord_W8zh_con_info'
/usr/local/lib/ghc-8.2.1/rts/libHSrts-ghc8.2.1.so: undefined reference to 'base_GHCziIOziException_cannotCompactPinned_closure'
...
По-видимому, включение проекта Haskell втягивает в него дополнительные файлы библиотеки. Как я могу явно зависеть от всего необходимого, чтобы избежать такой хрупкости?
Вывод скрипта сборки при включении вызова foo
с ldd
в конечном исполняемом файле:
++ cabal build
Preprocessing executable 'foo.so' for call-haskell-from-C-0.1.0.0..
Building executable 'foo.so' for call-haskell-from-C-0.1.0.0..
Linking a.out ...
Linking dist/build/foo.so/foo.so ...
++ g++ -I /usr/local/lib/ghc-8.2.1/include '-DFOO="dist/build/foo.so/foo.so-tmp/Foo_stub.h"' -c bar.c++ -o test.o
++ g++ test.o dist/build/foo.so/foo.so -L /usr/local/lib/ghc-8.2.1/rts -lHSrts-ghc8.2.1 -o test
++ env LD_LIBRARY_PATH=dist/build/foo.so:/usr/local/lib/ghc-8.2.1/rts: sh -c 'ldd ./test; ./test'
linux-vdso.so.1 => (0x00007fff23105000)
foo.so => dist/build/foo.so/foo.so (0x00007fdfc5360000)
libHSrts-ghc8.2.1.so => /usr/local/lib/ghc-8.2.1/rts/libHSrts-ghc8.2.1.so (0x00007fdfc52f8000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fdfc4dbe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdfc49f4000)
libHSbase-4.10.0.0-ghc8.2.1.so => /usr/local/lib/ghc-8.2.1/base-4.10.0.0/libHSbase-4.10.0.0-ghc8.2.1.so (0x00007fdfc4020000)
libHSinteger-gmp-1.0.1.0-ghc8.2.1.so => /usr/local/lib/ghc-8.2.1/integer-gmp-1.0.1.0/libHSinteger-gmp-1.0.1.0-ghc8.2.1.so (0x00007fdfc528b000)
libHSghc-prim-0.5.1.0-ghc8.2.1.so => /usr/local/lib/ghc-8.2.1/ghc-prim-0.5.1.0/libHSghc-prim-0.5.1.0-ghc8.2.1.so (0x00007fdfc3b80000)
libgmp.so.10 => /usr/lib/x86_64-linux-gnu/libgmp.so.10 (0x00007fdfc3900000)
libffi.so.6 => /usr/local/lib/ghc-8.2.1/rts/libffi.so.6 (0x00007fdfc36f3000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fdfc33ea000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fdfc31e2000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fdfc2fde000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fdfc2dc1000)
/lib64/ld-linux-x86-64.so.2 (0x00007fdfc5140000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fdfc2bab000)