Вызов Haskell из кода C ++ - PullRequest
       22

Вызов Haskell из кода C ++

52 голосов
/ 05 октября 2010

В настоящее время я пишу приложение на C ++ и обнаружил, что некоторые его функциональные возможности были бы лучше написаны на Haskell.Я видел инструкции по вызову Haskell из кода C , но возможно ли сделать то же самое с C ++?

EDIT: Чтобы уточнить, что япоиск - это способ скомпилировать код на Haskell во внешнюю библиотеку, которую g ++ может связать с объектным кодом из C ++.

ОБНОВЛЕНИЕ: Ниже приведен рабочий пример для всех остальныхинтересуюсь (тоже не забуду).

Ответы [ 5 ]

57 голосов
/ 05 октября 2010

Всем, кто заинтересован, это тот тестовый пример, с которым я наконец-то начал работать:


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
31 голосов
/ 05 октября 2010

Редактировать: Вы также должны увидеть ответ Томера ниже. Мой ответ здесь описывает теорию происходящего, но у меня могут быть неполные детали исполнения, тогда как его ответ - полный рабочий пример.

Как указывает 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 просто действует как GCC (и, IIUC, функционально - это GCC) при компиляции C-программы. Однако при его связывании он отличается от того, что происходит, если вы вызываете 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. Это должно привести к рабочей программе.

Кроме того, вы должны быть в состоянии сделать что-то похожее на -v исследование вывода с помощью GHC и выяснить, на какую библиотеку времени исполнения (или библиотеки) Haskell она ссылается для поддержки Haskell, а затем добавить эту библиотеку при компоновке вашей программы. с C ++, как вы уже делаете для чистых программ на C ++. (Подробности об этом см. В ответе Томера, поскольку он так и сделал.)

9 голосов
/ 17 сентября 2014

Это учебное пособие по теме:

https://github.com/jarrett/cpphs

Оно охватывает вызов Haskell из C ++ и вызов C из Haskell.

2 голосов
/ 05 октября 2010

Поскольку вы можете вызывать Haskell из C, нет причины, по которой вы не можете вызвать его из C ++.С другой стороны, вызов C ++ из Haskell намного сложнее и обычно требует оболочки C.

Редактировать, чтобы развернуть.Инструкции неверны, чтобы быть неполными.Это вики-страница.Посмотрите непосредственно на руководство GHC: http://www.haskell.org/ghc/docs/6.12.2/html/users_guide/ffi-ghc.html

Здесь описывается, как экспортировать функции и как использовать свой собственный основной.Обратите внимание, где написано «какой-то другой язык, скажем C.»Это объясняется тем, что вы можете сделать это на любом языке (и компиляторе), который может вызывать функции vanilla C, которые вы экспортируете и которые предоставляет HsFFI.h.Это не зависит от языка и от компилятора.Все, что для этого требуется, - это возможность вызывать функции C с использованием стандартных соглашений о вызовах в вашей системе, которые, безусловно, обеспечивает компилятор C ++ (например, g ++).

0 голосов
/ 06 апреля 2019

В cabal 2.0 добавлена ​​функция «посторонних библиотек», которая, кажется, решает проблемы компоновщика, а также делает весь процесс сборки намного более приятным.

Я собрал краткое учебное пособие https://github.com/pdlla/haskell-ffi-cabal-foreign-library-examples

...