How can I integrate a Haskell DLL into a C ++ / Qt Windows application?
I wrote some functions in Haskell and Id to create them in a DLL so that I can call them from my C ++ application. I'm just having a terrible time. (It looks like calling a C DLL from Haskell is much more common and better documented.)
My Haskell installation is MinGHC 7.10.1 (32 bit). The C ++ project uses Visual Studio 2010 and Qt 5.4; I am using qmake to create a VS project file from a .pro file. That's what I'm doing:
- Use cabal-install to install packages that I need libraries. (They are installed globally, not sandboxed.)
- Run
ghc -no-hs-main -v SpectrumMath.hs > ghc_output 2>&1
to output the GHC verbose build to a file. - Look at the file
ghc_output
and find the linker command (its line after "*** Linker:"). Extract the library directories (flagged-L
) and library names (flagged-L
). -
Add these libraries to your Qt.pro file like
LIBS += \ -Lsome_long_path_here \ -Lsome_other_long_path_here \ -lsome_library_name_here \ -lsome_other_library_name_here
-
Run
ghc --print-libdir
. Add/include
to the end of this path and add it to your .pro file:INCLUDEPATH += "C:/Program Files (x86)/MinGHC-7.10.1/ghc-7.10.1/lib/include"
-
Run qmake to create a Visual Studio project.
- Open your VS project. Open the project properties and go to "Configuration Properties" β "Linker" β enter and edit the "Additional Dependencies" field. Qmake helps "fix" the library names, so replace each instance of "HSsomething.lib" with "libHSsomething.a". Rename "Cffi-6.lib" to "libCffi-6.a" and "HSrts.lib" to "libHSrts.a" in the same way.
This crazy process comes from this wiki page . The C ++ file is supposed to be #include "SpectrumMath_stub.h"
, the file that was generated by GHC in step 2.
I'm stuck at this point because I don't know what to do with the m library (the one that came from the flag -lm
). Visual Studio cannot find "m.lib", but if I remove this library from the list, I get dozens of "unresolved external" messages for the library to be needed. How to tell Visual Studio about libm dependency? Or should I completely follow another process?
I recommend using cabal-install
(or stack
) for this. If you plan to distribute this DLL without LPGL compliant licensing, chances are you will want to use the GHC build using (the default) integer-simple
instead integer-gmp
.
Haskell side
Check out this GHC tutorial first .
After reading this, you know you need a copy StartEnd.c
. Let's say it's in src\
.
So here's your MyLibrary.cabal
file:
...other cabal stuff...
library
exposed-modules: MyModule
--other-modules:
other-extensions: ForeignFunctionInterface
build-depends: base
hs-source-dirs: src
c-sources: src/StartEnd.c --tells cabal to rebuild if this changes
default-language: Haskell2010
--default-extensions:
-- Add whatever options you need, in addition to these:
-- (In my experience, adding `-threaded` can make the DLL
-- behave better during debugging; but that only a guess.)
ghc-options: -O1 -shared src/StartEnd.c
Now create it with cabal build
(or stack build
if you are using stack
).
Wait for warnings. Cabal is not quite aware of what you are doing.
You will find three important files for you regarding your project:
-
HSdll.dll
-
HSdll.dll.a
(Windows usually calls these files.lib
, you can rename it if you like) -
build\...\MyLibrary_stub.h
Note that this DLL statically binds all Haskell dependencies. Therefore, you don't need to tell Visual Studio about anything other than your DLL.
C ++ side
- In C ++ code, you need to add the file
MyLibrary_stub.h
generated above. -
You will also need to specify prototypes
HsStart
andHsEnd
somewhere:extern "C" { void HsStart(); void HsEnd(); }
-
The file
_stub.h
contains some headers GHC, so you need to add a folder in the "Advanced Features" in the VS project:<ghc-folder>\ghc-7.10.1\lib\include
.- One trick for this is to run
where ghc
, dropghc.exe
and add\..\lib\include
to the output of this.
- One trick for this is to run
In a C ++ application, make sure you call HsStart()
and HsEnd()
at the beginning and end of your program. I like to encapsulate them in a RAII framework, so I can't forget to do it right.
My experience
I've tried this with versions cabal-install
1.2 * and stack
. I've used GHC 7.8.4 and 7.10.1, each with 32-bit and 64-bit.
Note that there Cabal
will be much better support for this soon .
Naming your DLL
Until Cabal gets better support, the only way I've found to give your DLL a name is add -o MyLibrary.dll
in ghc-options
. Even though it works, GHC gives you warnings when you do this, showing that it really doesn't know what's going on. To help you, you can pre-create StartEnd.c
in StartEnd.o
and link this. The command used looks something like this:
cabal exec -- ghc -optc-O -c src/StartEnd.c -o obj/StartEnd.o
Now, in ghc-options
, replace src/StartEnd.c
with obj/StartEnd.o
.
Replacing Cabal
with stack
should also work if you are using stack
.