Статическая ссылка библиотеки С++ с библиотекой Haskell
Настройка: у меня есть библиотека Haskell HLib
, которая делает вызовы на бэкэнд C/С++ CLib
для эффективности. Бэкэнд небольшой и специализирован для использования с HLib
. Интерфейс CLib
будет отображаться только через HLib
; Тесты HLib
, тесты HLib
и сторонние библиотеки в зависимости от HLib
не будут направлять прямые вызовы FFI на CLib
. С точки зрения тестирования/бенчмарка/третьей стороны, HLib
должен отображаться чисто Haskell. Это означает, что в разделах файла кэша для, например, тестов HLib
не должно быть ссылок на -lCLib
, libCLib
и т.д., Только build-depends
на HLib
, и что исполняемым файлам не нужно искать для динамической библиотеки CLib
. Мне нужно иметь возможность создавать и запускать все исполняемые файлы в HLib
и сторонних библиотеках, а также запускать cabal repl
для разработки.
Первоначально CLib
был написан в чистом C. Кабаль имеет поддержку для этого случая, и я могу интегрировать CLib
в HLib
точно так, как описано выше, используя include-dirs
, c-sources
и includes
в файле cabal.
CLib
превратился в библиотеку С++, и я не мог понять, как легко интегрировать кэбаль. Вместо этого я прибегал к make файлу с пользовательской сборкой и Setup.hs, например this. Вы можете увидеть небольшой пример этого метода здесь 1,2.
В этом примере я не могу запустить cabal repl
в HLib
, потому что "Загрузка архивов не поддерживается". Это действительно означает, что мне нужна динамическая библиотека С++, которая достаточно проста для создания (там есть прокомментированная строка в make файле CLib
). Однако, если я делаю динамическую С++-библиотеку, тест для HLib
завершается с ошибкой во время выполнения из-за отсутствия такого файла или каталога libclib.so. Это плохо (в дополнение к сбою), потому что тестовый исполняемый файл связан с динамической библиотекой, чего я не хочу.
Конкретно, тесты для HLib
и SimpleLib
должны пройти, и я должен иметь возможность запускать cabal repl
в каталогах HLib
и SimpleLib
.
Другие вещи, которые я пробовал: этот ответ, этот ответ (который я не могу получить для компиляции), this и чтение документов (приводит к ошибкам "перемещения" ).
Я использую GHC-7.10.3 на данный момент, хотя, если это значительно проще в 8.0, это прекрасно.
[1] Упрощено из lol/problems.
[2] Загрузите и запустите ./sandbox-init
. Это создает HLib
(который неявно строит CLib
и SimpleLib
, который является библиотекой Haskell, которая зависит от HLib
.
Ответы
Ответ 1
Включение библиотеки C или С++ с библиотекой Haskell тривиально, если вы знаете несколько трюков.
Я получил ядро из этой статьи хотя, похоже, это слишком сложно. Вы можете использовать cabal (в настоящее время 1.25) с типом сборки Simple
(т.е. Специальным Setup.hs
), без make файла и без внешних инструментов, таких как c2hs
.
Чтобы включить символы из чистой библиотеки C:
- В вашем файле кэша добавьте
Include-dirs: relative/path/to/headers/
или Includes: relative/path/to/myheader.h
.
- Добавить
C-sources: relative/path/to/csources/c1.c, relative/path/to/csources/c2.c, etc
.
Там есть пара дополнительных бит для С++:
- Вы можете добавить файлы
.cpp
в поле C-sources
в файле cabal.
- Для всех функций в файлах
.cpp
, к которым нужен доступ Haskell, добавьте extern "C"
, чтобы избежать изменения имени.
- Обведите весь не-чистый код в заголовочных файлах с помощью
#ifdef __cplusplus ... #endif
(см. n.m. answer).
- Если вы используете стандартную библиотеку С++, вам нужно добавить
extra-libraries: stdc++
в свой файл cabal и связать с g++
с помощью ghc-options: -pgmlg++
.
- Возможно, вам придется немного поиграть с порядком, в котором вы указываете файлы
.c(pp)
в файле cabal, если вы хотите, чтобы динамическая компоновка (т.е. cabal repl
) работала. Подробнее см. этот билет.
Что это! Вы можете увидеть полный рабочий пример здесь, который работает как с stack
, так и с cabal
.
Ответ 2
GHC не может понять файлы заголовков С++. Он нуждается в чистом C-коде. Общий способ для файла заголовка С++ для предоставления интерфейса C состоит в том, чтобы изолировать части С++ с помощью #ifdef __cplusplus
, например:
#ifdef __cplusplus
extern "C" { // C compilers and various C-based FFIs don't like this
#endif
void foo();
#ifdef __cplusplus
}
#endif
Кроме того, GHCi, как известно, имеет проблемы со связыванием кода на С++. Например, в какой-то момент он не понимал слабые символы (часто создаваемые компилятором в сочетании с встроенными функциями и экземплярами шаблонов). Возможно, вы столкнулись с одной из этих проблем. Я бы рекомендовал подать отчет об ошибке команде GHC.