c++haskellhaskell-stack

Haskell stack C++ linking fails


I am trying to write a Haskell library and am getting the error message in stack ghci:

ghci> functionThatCalls_cSmithNormalForm 0
ghc-9.4.5: ^^ Could not load 'csmith_normal_form', dependency unresolved. See top entry above.

GHC.ByteCode.Linker: can't find label
During interactive linking, GHCi couldn't find the following symbol:
  csmith_normal_form

whenever I call the function cSmithNormalForm which is defined in SNF.hs as:

foreign import ccall "csmith_normal_form" cSmithNormalForm :: Ptr CLLong -> IO (Ptr CLLong)

the C++ function is exported in the snf.cpp file (the only c++ file in the whole project) as:

using i64 = long long;
(...)
extern "C" {
    i64 *csmith_normal_form(i64[]);

    i64 *csmith_normal_form(i64 _mx[]) {
        (...)
    }
}

After a lot of attempts to make this link, my package.yaml file contains this:

cxx-sources:
- src/snf.cpp
cxx-options:
- -std=c++17

include-dirs:
- src

library:
  source-dirs: src
  cxx-sources:
  - src/snf.cpp
  cxx-options:
  - -std=c++17
  when:
    - condition: os(linux)
      extra-libraries: stdc++

The stack.yaml file has been left untouched. Both SNF.hs and snf.cpp lie in the same directory (src).

Despite the error, stack build runs successfully.

Is there a way to fix the error and successfully call the c++ function from Haskell? Also, is there any available documentation for how to use options such as cxx-options with stack? I was unable to find anything official looking.


Solution

  • This is a Stack bug. GHCi needs to have any standalone foreign .o files passed as arguments on the command line. Stack inspects the Cabal c-sources to determine an appropriate list of C .o files to supply, but it doesn't inspect the cxx-sources line.

    There are a couple possible workarounds.

    First, you could try to pass the correct filename yourself with something like:

    $ stack ghci --ghci-options $(find .stack-work -name 'snf.o')
    

    Second, you could consider specifying your C++ sources in a c-sources line instead of a cxx-sources line. Stack and Cabal are fine with C and C++ files being listed in c-sources, and they'll correctly identify which is which based on the file extension. The only reason for having a cxx-sources field in the first place is to allow different cc-options and cxx-options fields so C and C++ files can be compiled with different flags (see the documentation for cxx-sources).