www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Haskell calling D code through the FFI

reply "Jon" <jdgall84 gmail.com> writes:
TLDR -- Calling D code from Haskell through the FFI works with 
DMD but not with GDC or LDC2.

The time consuming version:

Since D allows C to directly call it, if the D code is wrapped in 
extern (C){ ... }, I thought it should be possible to call such D 
code from Haskell using the FFI.

The FFI in Haskell allows directly calling C code given a header 
file, the name of a function, and its C-type.  So I made a D file 
with the functions I want to call, FunctionsInD.d

     extern (C){
         int d_f(int x){
             return x+2;
         }
         int d_g(int x, int y){
             return x+y;
         }
     }

Then made a header file, FunctionsInD.h

     int d_f(int x);
     int d_g(int x, int y);

Then the Haskell wrapper ToD.hs:

     module ToD where

     import Foreign.C

     foreign import ccall "FunctionsInD.h d_f"
         c_f :: CInt -> CInt

     foreign import ccall "FunctionsInD.h d_g"
         c_g :: CInt -> CInt -> CInt

     h_f :: Int -> Int -- the "pure" haskell version
     h_f x = fromIntegral (c_f (fromIntegral x))

     h_g :: Int -> Int -> Int
     h_g x y = fromIntegral (c_g (fromIntegral x) (fromIntegral y))

For reasons I don't completely understand, you also need a fake 
main function, dummy.d:

     void main(){}

And a driver for test, Main.hs:

     module Main where

     import ToD

     main :: IO ()
     main = do
         let (a,b) = (h_f 3, h_g 3 4)
         sequence_ [putStrLn (show a), putStrLn (show b)]

To put this all together, here is the makefile.

     DC = dmd




     main: FunctionsInD.o FunctionsInD.h dummy.o
         ghc Main.hs --make -o main_ffi_test FunctionsInD.o 
dummy.o -lphobos2

     clean:
         rm FunctionsInD.o dummy.o main_ffi_test

     FunctionsInD.o FunctionsInD.d:
         $(DC) -c FunctionsInD.d

     dummy.o: dummy.d
         $(DC) -c dummy.d

The question: if I comment out DC=dmd, and uncomment any of the 
other options, the build fails.  The problem is that ghc 
complains of multiple definitions of main (not with DMD only GDC 
and LDC2).  If I remove dummy.o from the ghc line, I get, 
undefined reference to _DMain.

But wait!  It gets weirder.  I can compile FunctionsInD.d with 
GDC or LDC2, as long as I compile dummy.d with DMD, everything 
works fine.

Once I get through this hump, I will try to get on with more 
interesting interfaces, like strings and structs.
Aug 04 2014
next sibling parent reply "safety0ff" <safety0ff.dev gmail.com> writes:
Don't forget to call rt_init: 

Aug 04 2014
parent reply "Jon" <jdgall84 gmail.com> writes:
On Monday, 4 August 2014 at 21:10:46 UTC, safety0ff wrote:
 Don't forget to call rt_init: 

Where/when should I call this?
Aug 04 2014
parent reply "safety0ff" <safety0ff.dev gmail.com> writes:
On Monday, 4 August 2014 at 21:14:17 UTC, Jon wrote:
 On Monday, 4 August 2014 at 21:10:46 UTC, safety0ff wrote:
 Don't forget to call rt_init: 

Where/when should I call this?
Before calling any D functions, but usually it's simplest to call it early in main. It initializes the GC and notifies the D runtime of its existence. For simple D functions you might get away without calling it.
Aug 04 2014
parent reply "Jon" <jdgall84 gmail.com> writes:
I get Error: core.runtime.rt_init is private.  And Error: 
core.runtime.init is not accessible.

On Monday, 4 August 2014 at 21:22:37 UTC, safety0ff wrote:
 On Monday, 4 August 2014 at 21:14:17 UTC, Jon wrote:
 On Monday, 4 August 2014 at 21:10:46 UTC, safety0ff wrote:
 Don't forget to call rt_init: 

Where/when should I call this?
Before calling any D functions, but usually it's simplest to call it early in main. It initializes the GC and notifies the D runtime of its existence. For simple D functions you might get away without calling it.
Aug 04 2014
parent reply "safety0ff" <safety0ff.dev gmail.com> writes:
On Monday, 4 August 2014 at 21:35:21 UTC, Jon wrote:
 I get Error: core.runtime.rt_init is private.  And Error: 
 core.runtime.init is not accessible.
I would add them to the header and Haskell wrapper (FunctionsInD.h and ToD.hs.) The signatures are: int rt_init(); int rt_term(); When it is linked it will find the symbols in druntime.
Aug 04 2014
parent reply "Jon" <jdgall84 gmail.com> writes:
Yes, thank you.  That is exactly what I did.

On Monday, 4 August 2014 at 21:48:40 UTC, safety0ff wrote:
 On Monday, 4 August 2014 at 21:35:21 UTC, Jon wrote:
 I get Error: core.runtime.rt_init is private.  And Error: 
 core.runtime.init is not accessible.
I would add them to the header and Haskell wrapper (FunctionsInD.h and ToD.hs.) The signatures are: int rt_init(); int rt_term(); When it is linked it will find the symbols in druntime.
Aug 04 2014
parent "Jon" <jdgall84 gmail.com> writes:
As a note, I can interact with strings as expected, but working 
with structs looks like it will take a little bit of work.

On Monday, 4 August 2014 at 22:17:36 UTC, Jon wrote:
 Yes, thank you.  That is exactly what I did.

 On Monday, 4 August 2014 at 21:48:40 UTC, safety0ff wrote:
 On Monday, 4 August 2014 at 21:35:21 UTC, Jon wrote:
 I get Error: core.runtime.rt_init is private.  And Error: 
 core.runtime.init is not accessible.
I would add them to the header and Haskell wrapper (FunctionsInD.h and ToD.hs.) The signatures are: int rt_init(); int rt_term(); When it is linked it will find the symbols in druntime.
Aug 04 2014
prev sibling parent reply "David Soria Parra" <davidsp fb.com> writes:
On Monday, 4 August 2014 at 20:48:09 UTC, Jon wrote:

 For reasons I don't completely understand, you also need a fake 
 main function, dummy.d:

     void main(){}
Note that this is not necessary if you compile with -lib e.g.: dmd -lib -oflibtest.a test.d and then ghc Main.hs --make -omain libtest.a I don't have gdc or ldc installed but as far as I know ldc has a -lib flag as well.
Aug 05 2014
next sibling parent "Jon" <jdgall84 gmail.com> writes:
Oh great thank you.  I think that might solve the majority of the 
confusion I was having.  One thing I can't figure out though, is 
getting garbage collection to work as expected.  If I have a 
function that allocates a pointer to a struct using new, I get an 
error on linking _dAlloc... not found.  But maybe compiling as a 
lib will solve this too.

On Tuesday, 5 August 2014 at 21:28:08 UTC, David Soria Parra 
wrote:
 On Monday, 4 August 2014 at 20:48:09 UTC, Jon wrote:

 For reasons I don't completely understand, you also need a 
 fake main function, dummy.d:

    void main(){}
Note that this is not necessary if you compile with -lib e.g.: dmd -lib -oflibtest.a test.d and then ghc Main.hs --make -omain libtest.a I don't have gdc or ldc installed but as far as I know ldc has a -lib flag as well.
Aug 05 2014
prev sibling parent reply "Jon" <jdgall84 gmail.com> writes:
So that does indeed solve some of the problems.  However, using 
this method, when linking I get two errors, undefined reference 
rt_init() and rt_term() I had just put these methods in the 
header file.  If I put wrappers around these functions and export 
I get the rt_init, rt_term is private.

On Tuesday, 5 August 2014 at 21:28:08 UTC, David Soria Parra 
wrote:
 On Monday, 4 August 2014 at 20:48:09 UTC, Jon wrote:

 For reasons I don't completely understand, you also need a 
 fake main function, dummy.d:

    void main(){}
Note that this is not necessary if you compile with -lib e.g.: dmd -lib -oflibtest.a test.d and then ghc Main.hs --make -omain libtest.a I don't have gdc or ldc installed but as far as I know ldc has a -lib flag as well.
Aug 05 2014
next sibling parent "safety0ff" <safety0ff.dev gmail.com> writes:
On Tuesday, 5 August 2014 at 23:23:43 UTC, Jon wrote:
 So that does indeed solve some of the problems.  However, using 
 this method, when linking I get two errors, undefined reference 
 rt_init() and rt_term() I had just put these methods in the 
 header file.  If I put wrappers around these functions and 
 export I get the rt_init, rt_term is private.
It works for me, here are the main parts of my Makefile: DC = ~/bin/dmd main: Main.hs FunctionsInD.a ghc -o main Main.hs FunctionsInD.a ~/lib/libphobos2.a -lpthread FunctionsInD.a: FunctionsInD.d $(DC) -c -lib FunctionsInD.d I passed in the phobos object directly because I don't know how to specify the "~/lib" directory on the ghc command line.
Aug 05 2014
prev sibling parent reply David Soria Parra via Digitalmars-d-learn writes:
Jon via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:

 So that does indeed solve some of the problems.  However, using this
 method, when linking I get two errors, undefined reference rt_init()
 and rt_term() I had just put these methods in the header file.  If I
 put wrappers around these functions and export I get the rt_init,
 rt_term is private.
rt_init is part of druntime. You need to link druntime into your program in order to make it work.
Aug 06 2014
parent "Jon" <jdgall84 gmail.com> writes:
Hi, thank you!! I have modified the program based on a previous 
suggestion.  rt_init is called before using any D functionality 
and rt_term is called after using D functionality.  I did this by:
   1) Placing int rt_init(); and int rt_term(); into the header 
file that Haskell reads
   2) Creating Haskell stubs
    foreign import ccall unsafe "FunctionsInD.h rt_init"
        d_init :: IO CInt
    foreign import ccall unsafe "FunctionsInD.h rt_term"
        d_term :: IO CInt
And then in the Main haskell program, in main, the function 
starts with
     d_init
and ends with
     d_term
I'm pretty sure this is working nicely, because I can allocate 
structs with the "new" keyword in D, and this led to segfaults 
before using rt_init and rt_term.

I think the problem I was having was trying to do this in a 
stupid way i.e. put wrappers around init and term on the D side.

However, I still do not know how to compile without the using a 
fake main.  Compiling with -c -lib does still give me a _Dmain 
undefined reference.  I did
     dmd -c -lib FunctionsInD.d
     ghc --make Main.hs FunctionsInD.a -lphobos2
And get
     
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/libphobos2.so: 
undefined reference to `_Dmain'

On Wednesday, 6 August 2014 at 11:03:33 UTC, David Soria Parra 
via Digitalmars-d-learn wrote:
 Jon via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> 
 writes:

 So that does indeed solve some of the problems.  However, 
 using this
 method, when linking I get two errors, undefined reference 
 rt_init()
 and rt_term() I had just put these methods in the header file.
  If I
 put wrappers around these functions and export I get the 
 rt_init,
 rt_term is private.
rt_init is part of druntime. You need to link druntime into your program in order to make it work.
Aug 06 2014