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