Setup: I have a Haskell library HLib
which makes calls to a C/C++ backend CLib
for efficiency. The backend is small and specialized for use with HLib
. The interface to CLib
will only be exposed through HLib
; HLib
tests, HLib
benchmarks and third party libraries depending on HLib
will not make direct FFI calls to CLib
. From the test/benchmark/3rd party lib perspective, HLib
should be appear purely Haskell. This means in the cabal file sections for, e.g., HLib
tests, there should be no references to -lCLib
, libCLib
, etc, only a build-depends
on HLib
, and that executables should not need to look for a dynamic CLib
library. I need to be able to build and run all executables in HLib
and third-party libs, as well as run cabal repl
for development.
Originally, CLib
was written in pure C. Cabal has support for this case, and I can integrate CLib
into HLib
in precisely the manner described above by using include-dirs
, c-sources
, and includes
fields in the cabal file.
CLib
has evolved into a C++ library, which I couldn't figure out how to get cabal to integrate easily. Instead, I resorted to a makefile with custom build and Setup.hs, like this. You can see a small example of this method here1,2.
In that example, I can't run cabal repl
in HLib
because "Loading archives not supported". This really means I need a dynamic C++ library, which is simple enough to create (there's a commented line in the CLib
makefile to do it). If I do make the dynamic C++ library, however, the test for HLib
fails at runtime due to a "no such file or directory libclib.so". This is bad (in addition to the crash) because the test executable linked against the dynamic library, which is not what I want.
Concretely, the tests for HLib
and SimpleLib
should both pass, and I should be able to run cabal repl
in both the hlib
and simplelib
directories.
Other things I've tried: this answer, this answer (which I can't get to compile), this, and reading the docs (results in "relocation" errors).
I'm using GHC-7.10.3 at the moment, though if this is significantly easier in 8.0, that's fine.
[1] Simplified from lol/challenges.
[2] Download and run ./sandbox-init
. This builds HLib
(which implicitly builds CLib
, and SimpleLib
, which is a Haskell library that depends on HLib
.
Including a C or C++ library with a Haskell library is trivial once you know a few tricks.
I got the core from this article, though it seems to overcomplicate things. You can use cabal (currently 1.25) with a Simple
build type (i.e. no special Setup.hs
), no makefile, and no external tools like c2hs
.
To include symbols from a pure C library:
Include-dirs: relative/path/to/headers/
or Includes: relative/path/to/myheader.h
. C-sources: relative/path/to/csources/c1.c, relative/path/to/csources/c2.c, etc
.There's a couple of extra bits for C++:
.cpp
files to the C-sources
field in the cabal file. .cpp
files that Haskell needs access to, add extern "C"
to avoid name mangling.#ifdef __cplusplus ... #endif
(see n.m.'s answer).extra-libraries: stdc++
to your cabal file, and link with g++
using ghc-options: -pgmlg++
..c(pp)
files in the cabal file if you want dynamic linking (i.e. cabal repl
) to work. See this ticket for more information.That's it! You can see a complete working example here which works with both stack
and cabal
.
GHC cannot really understand C++ header files. It needs pure C code. The common way for a C++ header file to provide C interface is to isolate away C++ parts with #ifdef __cplusplus
, e.g.:
#ifdef __cplusplus
extern "C" { // C compilers and various C-based FFIs don't like this
#endif
void foo();
#ifdef __cplusplus
}
#endif
In addition, GHCi is historically known to have problems with linking C++ code. For example, at one point it didn't understand weak symbols (often produced by the compiler in conjunction with inline functions and template instantiations). You might be seeing one of these problems. I'd recommend filing a bug report to the GHC team.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With