62

I'm currently writing an app in C++ and found that some of its functionality would be better written in Haskell. I've seen instructions on calling Haskell from C code, but is it possible to do the same with C++?

EDIT: To clarify, what I'm looking for is a way to compile Haskell code into an external library that g++ can link with the object code from C++.

UPDATE: I've put up a working example below for anyone else interested (also so I won't forget).

3
  • 2
    Have you tried it? That is to say, when you follow the C language directions from C++ code, does it work? Unless the guys preparing these instructions forgot extern "C", rely heavily on implicit conversion of void pointers, or named some variable class etc., I don't see why not.
    – asveikau
    Commented Oct 4, 2010 at 21:46
  • Because according to the instructions, I have to compile the C code with ghc. Commented Oct 4, 2010 at 23:38
  • The complete example in wiki.haskell using g++ to link does not work any more. The new method seems to rely on ghc doing all compilation and linking. On ArchLinux: ghc --make -dynamic -no-hs-main test.cpp Foo.hs -lstdc++ -o test Commented Jun 5, 2020 at 16:22

5 Answers 5

67

To anyone interested, this is the test case that I've finally got working:


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;
}

I did the compiles & linking on my Windows machine. The commands to run (in this order) are:

>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

The long list of parameters for the last g++ command is from running

>ghc M.hs -v

and then copying the command where it says "***Linker:" (some of the first parameters need to be removed).


The result:

>test
hello
22
3
  • 10
    I got this to work on Linux as well. But, I also discovered you can drastically simplify the final step by linking with GHC instead of G++ (because the manual linking step is so much simpler for C++). Specifically you can link with: ghc -no-hs-main M.o test.o -lstdc++
    – Onyxite
    Commented Jan 29, 2014 at 18:36
  • 1
    Also, for those on linux who installed haskell via apt-get ghc, the appropriate include directory should be available with -I/usr/lib/ghc/include. If you're c++ program still can't find HsFFI.h, try searching elsewhere for HsFFI.h on your system, perhaps using find or locate. Commented May 1, 2014 at 0:52
  • 1
    Agree to the tip by @Onyxite. As a tribute to the Haskell fan club, I created below a video about it: asciinema.org/a/jl6B8b327H8knPo9gMWEupWkB
    – daparic
    Commented Oct 26, 2018 at 3:47
41

Edit: You should also see Tomer's answer below. My answer here describes the theory of what's going on, but I may have some of the details of execution incomplete, whereas his answer is a complete working example.

As sclv indicates, compiling should be no problem. The difficulty there is likely to be linking the C++ code, and here you will have a little bit of difficulty with getting all the needed runtime libraries linked in. The problem is that Haskell programs need to be linked with the Haskell runtime libraries, and C++ programs need to be linked with the C++ runtime libraries. In the Wiki page you reference, when they do

$ ghc -optc -O test.c A.o A_stub.o -o test

to compile the C program, that actually does two steps: It compiles the C program into an object file, and then links it together. Written out, that would be something like (probably not quite right, as I don't speak GHC):

$ ghc -c -optc-O test.c -o test.o
$ ghc test.o A.o A_stub.o -o test

GHC just acts like GCC (and, IIUC, functionally is GCC) when compiling the C program. When linking it, however, it is different from what happens if you call GCC directly, because it also magically includes the Haskell runtime libraries. G++ works the same way for C++ programs -- when it's used as a linker, it includes the C++ runtime libraries.

So, as I mentioned, you need to compile in a way that links with both runtime libraries. If you run G++ in verbose mode to compile and link a program, like so:

$ g++ test.cpp -o test -v

it will create a long list of output about what it's doing; at the end will be a line of output where it does the linking (with the collect2 subprogram) indicating what libraries it links to. You can compare that to the output for compiling a simple C program to see what's different for C++; on my system, it adds -lstdc++.

Thus, you should be able to compile and link your mixed Haskell/C++ program like so:

$ 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

There, because you've specified -lstdc++, it will include the C++ runtime library (assuming -l is the right GHC syntax; you'll need to check), and because you've linked with ghc, it will include the Haskell runtime library. This should result in a working program.

Alternately, you should be able to do something similar to the -v output investigation with GHC, and figure out what Haskell runtime library (or libraries) it links to for Haskell support, and then add that library when linking your program with C++, just as you already do for pure C++ programs. (See Tomer's answer for details of that, since that's what he did.)

5
  • Thanks for the detailed answer. Following your instructions, I get "only" 1 undefined reference (for std::__ostream_insert) , so this is certainly an improvement. I guess now it's only a matter of coordinating all the necessary libraries, which I'll leave for tomorrow. The -v and -l options do work with GHC. The only thing is that the flag -XForeignFunctionInterface is needed for the first ghc command (and also some include directories with the -I flag) Commented Oct 5, 2010 at 1:08
  • 2
    @Tomer: Alternatively (to -XForeignFunctionInterface on the command line) you can place a language pragma at the top of any haskell sources that use the FFI: {-# LANGUAGE ForeignFunctionInterface #-}
    – mokus
    Commented Oct 5, 2010 at 14:59
  • for some reason the A_stub.o isn't getting compiled for me , i'm on linux other then that everything is compiles fine
    – pyCthon
    Commented Oct 9, 2012 at 18:31
  • 1
    @pyCthon: Did you use the "-XForeignFunctionInterface" option that Tomer mentions when compiling the "A.hs" program? (I edited the answer just now to add that; I'd omitted it before.) Commented Oct 14, 2012 at 5:23
  • You can also put this {-# LANGUAGE ForeignFunctionInterface #-} on top of haskell code. And then, you no longer need to use the -XForeignFunctionInterface option.
    – daparic
    Commented Oct 26, 2018 at 4:02
11

This is a tutorial on the topic:

https://github.com/jarrett/cpphs

It covers calling Haskell from C++ and calling C from Haskell.

0
4

cabal 2.0 added the "foreign-library" feature which seems to solve the linker issues as well as making the whole build process much more pleasant in general.

I put together a short example tutorial https://github.com/pdlla/haskell-ffi-cabal-foreign-library-examples

2

Since you can call Haskell from C, there's no reason you can't call it from C++. Calling C++ from Haskell, on the other hand, is much harder and usually requires a C wrapper.

Edit to expand. The instructions are wrong to incomplete. They're a wiki page. Look directly at the GHC manual: http://www.haskell.org/ghc/docs/6.12.2/html/users_guide/ffi-ghc.html

This describes how to export functions, and how to use your own main. Note where it says "some other language, say C." It says this because you can do this from any language (and compiler) that can invoke the vanilla C functions that you're exporting, and that HsFFI.h provides. This is language agnostic, and compiler agnostic. All it requires is the ability to invoke C functions using the standard calling conventions on your system, which a C++ compiler (such as g++) certainly provides.

6
  • According to the instructions, I have to compile the C code with ghc. A solution that would work with C++ is one where the code is eventually compiled with gcc/g++ Commented Oct 4, 2010 at 23:39
  • @Tomer Vromen: see my edit above. What you want is possible out of the box, and documented directly in the GHC manual.
    – sclv
    Commented Oct 5, 2010 at 0:07
  • Clarified a bit as well -- you would compile the C++ code with gcc/g++, and the Haskell code with GHC. Commented Oct 5, 2010 at 0:28
  • Sorry I'm so dense, but I'm just getting a bunch of linker errors. I tried linking with the .o files that ghc produced, and also with all sorts of libraries from GHC's lib path. I always get a message that hs_init is an undefined reference. Is there a working step-by-step example somewhere? Commented Oct 5, 2010 at 0:47
  • Tomer - no problem! I just realized that this would be an issue, after I posted the comment. Hopefully my full answer will clarify that. (Unfortunately, I'm not aware of any step-by-step examples, but then, I'm only a C++ user at this point.) Commented Oct 5, 2010 at 0:52

Not the answer you're looking for? Browse other questions tagged or ask your own question.