44
votes

I would like to use an external library, RDFox, in a Haskell project.

Context: I am working on Windows and Linux, both 64 bits, using GHC 7.10 and stack. RDFox is programmed in C++. RDFox shared libraries (.dll, .so) can be downloaded with Java and Python wrappers.

Aim: I would like to reuse the compiled libraries from RDFox (.dll, .so) in my project in Haskell, so I need to create a Haskell wrapper for RDFox.

Issues: Being relatively new to Haskell, I have difficulty to know where to start. I found several pages about the subject (from the Haskell wiki and StackOverflow), but the workflow and configuration are not clear to me.

Questions: I would like to know:

  1. How to configure stack and cabal to use external library, to build on Windows or Linux (different machines, same repository).
  2. How to configure GHCi for interactive testing on this external library.
  3. Is the translation of the Python wrapper to Haskell the best way to go? I would like to avoid the analysis of the RDFox C++ code.
3
First you probably need to create a C-exported wrapper for the C++ api.alternative
Naming conventions in C++ are much more complicated than in C. I don't think FFI supports the name mangling for non-extern-C things.alternative
If you know the name-mangling scheme you can use System.Win32.DLL from the Win32 package and System.Posix.DynamicLinker from the unix package. However, the name-mangling will probably differ between the two platforms, so you have to do the work twice. If you write a C wrapper, you only have to do it once.Tobias Brandt
Just learn to love extern "C"{} in C++.bmargulies
You don't need to create any C wrapper. Necessary extern "C" is already in place (one would have a hard time creating the Python wrapper without it).n. 1.8e9-where's-my-share m.

3 Answers

2
votes
  1. You'll need to use extra-lib-dirs and extra-libraries in the executable section of your .cabal file like so:

    name:                 MyApp
    version:              0.1.0.0
    synopsis:
    homepage:
    author:               simon.bourne
    category:
    build-type:           Simple
    cabal-version:        >=1.10
    
    library
      exposed-modules:      HelloWorld
      build-depends:        base >= 4.7 && < 5
      hs-source-dirs:       src
      default-language:     Haskell2010
    
    executable MyApp
      main-is:              Main.hs
      extra-lib-dirs:       lib
      extra-libraries:      helloWorld
      build-depends:        base >= 4.7 && < 5,
                            MyApp
      hs-source-dirs:       app
    
    default-language: Haskell2010
    

    Put your dll and .so in lib. Be warned, you'll run into link order problems if you use a static library (.a instead of .so) on linux.

    See this for an example. Don't be fooled by the name as it works fine with .so files.

  2. stack ghci should just work provided it can find your library (LD_LIBRARY_PATH on linux).

  3. The C API (mentioned in the comments on your question) is already there. You just need to write the Haskell FFI signatures, for example:

    foreign import ccall safe "helloWorld" c_helloWorld :: IO ()
    

    I'd very strongly advise using safe ccalls, and don't wrap the functions in unsafePerformIO.

    If you need to pass non opaque structs, you might want to investigate c2hs or hsc2hs, but I don't think you'll need to. See this question for more details.

1
votes

You need to create a C-exported wrapper for the C++ api and Haskell wrapper to FFI to C-exported wrapper.

Marshaling between C# and Haskell described here: Calling Haskell from C#

but it very similar marshaling between C++ and Haskell

For example, create C++ export function:

extern "C" __declspec(dllexport) int __cdecl addFive(int number);

extern "C" __declspec(dllexport) int __cdecl addFive(int number)
{
    return number + 5;
}

In Haskell you need import code:

foreign import ccall "addFive" addFive :: Int -> Int

Then you can use addFive in Haskell as typical Haskell-function

For compbound data types (classes and structures) you need create C++ data-type analog in Haskell. Then you need describe how marshal data types from C++ to Haskell and from Haskell to C++.

In Haskell it means that you need create Storable instance for you data types.

0
votes

Answer from this documentation:

ghc -c Adder.hs
ghc -c StartEnd.c
ghc -shared -o Adder.dll Adder.o Adder_stub.o StartEnd.o