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?

+3


source to share


1 answer


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

    and HsEnd

    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

      , drop ghc.exe

      and add \..\lib\include

      to the output of this.

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

.

+3


source







All Articles