Jump to page: 1 2
Thread overview
Haskell calling D code through the FFI
Aug 04, 2014
Jon
Aug 04, 2014
safety0ff
Aug 04, 2014
Jon
Aug 04, 2014
safety0ff
Aug 04, 2014
Jon
Aug 04, 2014
safety0ff
Aug 04, 2014
Jon
Aug 05, 2014
Jon
Aug 05, 2014
David Soria Parra
Aug 05, 2014
Jon
Aug 05, 2014
Jon
Aug 06, 2014
safety0ff
Aug 06, 2014
David Soria Parra
Aug 06, 2014
Jon
August 04, 2014
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
    # DC = ldc2
    # DC = ldmd2
    # DC = gdc

    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.
August 04, 2014
Don't forget to call rt_init: http://dlang.org/phobos/core_runtime.html#.rt_init
August 04, 2014
On Monday, 4 August 2014 at 21:10:46 UTC, safety0ff wrote:
> Don't forget to call rt_init: http://dlang.org/phobos/core_runtime.html#.rt_init

Where/when should I call this?
August 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:
>> Don't forget to call rt_init: http://dlang.org/phobos/core_runtime.html#.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.
August 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:
>>> Don't forget to call rt_init: http://dlang.org/phobos/core_runtime.html#.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.

August 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.
August 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.

August 05, 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.

August 05, 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.
August 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.

« First   ‹ Prev
1 2