digitalmars.D.learn - Haskell calling D code through the FFI
- Jon (65/65) Aug 04 2014 TLDR -- Calling D code from Haskell through the FFI works with
- safety0ff (2/2) Aug 04 2014 Don't forget to call rt_init:
- Jon (2/4) Aug 04 2014 Where/when should I call this?
- safety0ff (5/9) Aug 04 2014 Before calling any D functions, but usually it's simplest to call
- David Soria Parra (7/10) Aug 05 2014 Note that this is not necessary if you compile with -lib e.g.:
- Jon (8/20) Aug 05 2014 Oh great thank you. I think that might solve the majority of the
- Jon (7/19) Aug 05 2014 So that does indeed solve some of the problems. However, using
- safety0ff (9/14) Aug 05 2014 It works for me, here are the main parts of my Makefile:
- David Soria Parra via Digitalmars-d-learn (3/8) Aug 06 2014 rt_init is part of druntime. You need to link druntime into your program
- Jon (31/46) Aug 06 2014 Hi, thank you!! I have modified the program based on a previous
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
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
On Monday, 4 August 2014 at 21:14:17 UTC, Jon wrote:On Monday, 4 August 2014 at 21:10:46 UTC, safety0ff wrote: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.Don't forget to call rt_init:Where/when should I call this?
Aug 04 2014
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: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.Don't forget to call rt_init:Where/when should I call this?
Aug 04 2014
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
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
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
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
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
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
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
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
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









"Jon" <jdgall84 gmail.com> 