www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - C binding with D function

reply llaine <darksioul6 gmail.com> writes:
Hi guys,

I'm trying to make a bridge between D and ruby with a gem called 
ffi.
It's basically a loading/binding library that grab C function and 
make them callable from Ruby code.

As you might already understand, i'm trying to develop extensions 
using D and uses them in Ruby.

I tried to get the simplest example ever but nothing is working 
so far.

Basically here is my D code :

import std.stdio : writeln;

extern(C)
{
   void puts(string str)
   {
     writeln(str);
   }
}

void main(){ }

I'm building it using "targetType": "dynamicLibrary" on my 
dub.json


My problem is when I try to call it in C, it blows up with a 
segmentation fault.
I'm looking for here extra documentation/help on how to properly 
create C binding in D.

I already post an issue on FFI to seek from help, you can find 
here some extra informations about the C trace


https://github.com/ffi/ffi/issues/522


Any help would be welcomed!
Aug 03 2016
next sibling parent reply bachmeier <no spam.net> writes:
On Wednesday, 3 August 2016 at 13:44:46 UTC, llaine wrote:
 Hi guys,

 I'm trying to make a bridge between D and ruby with a gem 
 called ffi.
 It's basically a loading/binding library that grab C function 
 and make them callable from Ruby code.

 As you might already understand, i'm trying to develop 
 extensions using D and uses them in Ruby.

 I tried to get the simplest example ever but nothing is working 
 so far.

 Basically here is my D code :

 import std.stdio : writeln;

 extern(C)
 {
   void puts(string str)
   {
     writeln(str);
   }
 }

 void main(){ }

 I'm building it using "targetType": "dynamicLibrary" on my 
 dub.json


 My problem is when I try to call it in C, it blows up with a 
 segmentation fault.
 I'm looking for here extra documentation/help on how to 
 properly create C binding in D.

 I already post an issue on FFI to seek from help, you can find 
 here some extra informations about the C trace


 https://github.com/ffi/ffi/issues/522


 Any help would be welcomed!
Probably because you need the D runtime. One way is to import core.runtime and call Runtime.initialize().
Aug 03 2016
parent reply llaine <darksioul6 gmail.com> writes:
On Wednesday, 3 August 2016 at 13:49:36 UTC, bachmeier wrote:

 Probably because you need the D runtime. One way is to import 
 core.runtime and call Runtime.initialize().
Where should I call this Runtime.initialize() ?
Aug 03 2016
parent reply bachmeier <no spam.net> writes:
On Wednesday, 3 August 2016 at 14:01:34 UTC, llaine wrote:
 On Wednesday, 3 August 2016 at 13:49:36 UTC, bachmeier wrote:

 Probably because you need the D runtime. One way is to import 
 core.runtime and call Runtime.initialize().
Where should I call this Runtime.initialize() ?
Does the second answer to this question http://stackoverflow.com/questions/676498/haskell-binding-with-ruby-through-ffi help? It's Haskell, but I think the hs_init function call is equivalent to Runtime.initialize. I've never done this sort of thing with Ruby so I might be missing something.
Aug 03 2016
parent reply llaine <darksioul6 gmail.com> writes:
On Wednesday, 3 August 2016 at 14:58:04 UTC, bachmeier wrote:
 On Wednesday, 3 August 2016 at 14:01:34 UTC, llaine wrote:
 On Wednesday, 3 August 2016 at 13:49:36 UTC, bachmeier wrote:

 Probably because you need the D runtime. One way is to import 
 core.runtime and call Runtime.initialize().
Where should I call this Runtime.initialize() ?
Does the second answer to this question http://stackoverflow.com/questions/676498/haskell-binding-with-ruby-through-ffi help? It's Haskell, but I think the hs_init function call is equivalent to Runtime.initialize. I've never done this sort of thing with Ruby so I might be missing something.
So basically I have to create wrapper.c ? I guess my D code is not valid. For example, if I call it directly from a C program it would also breaks for sure. Is there any good documentation on it? I've look at the official documentation, but i'm afraid it might be too advanced for me.
Aug 03 2016
next sibling parent llaine <darksioul6 gmail.com> writes:
On Wednesday, 3 August 2016 at 15:08:51 UTC, llaine wrote:
 On Wednesday, 3 August 2016 at 14:58:04 UTC, bachmeier wrote:
On Wednesday, 3 August 2016 at 15:08:51 UTC, llaine wrote: by switching my file to just this extern(C) { char* foo(char* str) { return str; } } It works. But it's really ... simple ahah I was expecting to have more advanced use.
Aug 03 2016
prev sibling parent reply Kagamin <spam here.lot> writes:
On Wednesday, 3 August 2016 at 15:08:51 UTC, llaine wrote:
 So basically I have to create wrapper.c ?
Yes, but you should write it in D. Runtime initialization is at https://dlang.org/phobos/core_runtime.html#.Runtime
Aug 03 2016
parent reply llaine <darksioul6 gmail.com> writes:
On Wednesday, 3 August 2016 at 15:14:24 UTC, Kagamin wrote:
 On Wednesday, 3 August 2016 at 15:08:51 UTC, llaine wrote:
 So basically I have to create wrapper.c ?
Yes, but you should write it in D. Runtime initialization is at https://dlang.org/phobos/core_runtime.html#.Runtime
Okay I tried to do something like this import std.stdio; import core.runtime; extern(C) { void foo(string str) { writeln(str); } } void main(){ Runtime.initialize(); Runtime.terminate(); } Am I doing it wrong ? Still getting a Segmentation fault
Aug 03 2016
next sibling parent reply llaine <darksioul6 gmail.com> writes:
On Wednesday, 3 August 2016 at 15:24:56 UTC, llaine wrote:
 On Wednesday, 3 August 2016 at 15:14:24 UTC, Kagamin wrote:
After digging a bit more, I get a different error. import std.stdio; import core.runtime; extern(C) { void initialize() { Runtime.initialize(); } void foo(string str) { writeln(str); } void terminate() { Runtime.terminate(); } } on my code ofc I call the three methods and I get uncaught exception dwarfeh(224) fatal error Abandon (core dumped)
Aug 03 2016
parent lobo <swamplobo gmail.com> writes:
On Wednesday, 3 August 2016 at 15:41:55 UTC, llaine wrote:

   void foo(string str)
   {
     writeln(str);
   }
shouldn't foo be: void foo(char* str) { import std.string; writeln(str.fromStringz); } bye, lobo
Aug 03 2016
prev sibling parent reply bachmeier <no spam.net> writes:
On Wednesday, 3 August 2016 at 15:24:56 UTC, llaine wrote:
 On Wednesday, 3 August 2016 at 15:14:24 UTC, Kagamin wrote:
 On Wednesday, 3 August 2016 at 15:08:51 UTC, llaine wrote:
 So basically I have to create wrapper.c ?
Yes, but you should write it in D. Runtime initialization is at https://dlang.org/phobos/core_runtime.html#.Runtime
Okay I tried to do something like this import std.stdio; import core.runtime; extern(C) { void foo(string str) { writeln(str); } } void main(){ Runtime.initialize(); Runtime.terminate(); } Am I doing it wrong ? Still getting a Segmentation fault
I have no way to test this, but translating the example to D, I get this: import core.runtime; import std.stdio; extern(C) { example_init() { Runtime.initialize(); } example_exit() { Runtime.terminate(); } void foo(int x) { writeln(2*x); } } // No main // compile as libffi-example.so Then in Ruby: module Example extend DL::Importable dlload "./libffi-example.so" extern "void example_init()" extern "void example_exit()" extern "void foo(int)" end Example.example_init Example.foo(3) Example.example_exit
Aug 03 2016
parent reply llaine <darksioul6 gmail.com> writes:
On Wednesday, 3 August 2016 at 15:47:48 UTC, bachmeier wrote:
 On Wednesday, 3 August 2016 at 15:24:56 UTC, llaine wrote:
 On Wednesday, 3 August 2016 at 15:14:24 UTC, Kagamin wrote:
 On Wednesday, 3 August 2016 at 15:08:51 UTC, llaine wrote:
 So basically I have to create wrapper.c ?
Yes, but you should write it in D. Runtime initialization is at https://dlang.org/phobos/core_runtime.html#.Runtime
// No main // compile as libffi-example.so Then in Ruby: module Example extend DL::Importable dlload "./libffi-example.so" extern "void example_init()" extern "void example_exit()" extern "void foo(int)" end Example.example_init Example.foo(3) Example.example_exit
Okay on stack overflow, they are not using ffi but dl. I tried changing ffi to dl, it's the same don't work unfortunatly.
Aug 03 2016
next sibling parent reply bachmeier <no spam.net> writes:
On Wednesday, 3 August 2016 at 15:56:34 UTC, llaine wrote:
 Okay on stack overflow, they are not using ffi but dl.
 I tried changing ffi to dl, it's the same don't work 
 unfortunatly.
That's about as far as I can go without having it set up on my machine. The procedure should be the same as I spelled out though. You need to create an extern(C) function that calls Runtime.initialize(). Then you call that function from Ruby the same as any other function you call with FFI. I'm familiar with the R FFI and the only difference is that some parts of that are automated.
Aug 03 2016
parent reply llaine <darksioul6 gmail.com> writes:
On Wednesday, 3 August 2016 at 16:09:15 UTC, bachmeier wrote:
 On Wednesday, 3 August 2016 at 15:56:34 UTC, llaine wrote:
 Okay on stack overflow, they are not using ffi but dl.
 I tried changing ffi to dl, it's the same don't work 
 unfortunatly.
That's about as far as I can go without having it set up on my machine. The procedure should be the same as I spelled out though. You need to create an extern(C) function that calls Runtime.initialize(). Then you call that function from Ruby the same as any other function you call with FFI. I'm familiar with the R FFI and the only difference is that some parts of that are automated.
Thank's for the help. By creating two methods initialize/terminate using Runtime.initialize() it's crashing with this strange error. I looked at the core.runtime source file and I saw a C binding for thoses one : https://github.com/dlang/druntime/blob/master/src/core/runtime.d#L36 /// C interface for Runtime.initialize, returns 1/0 instead of bool extern(C) int rt_init(); /// C interface for Runtime.terminate, returns 1/0 instead of bool extern(C) int rt_term(); Any idea how can I call them ?
Aug 04 2016
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 4 August 2016 at 10:36:05 UTC, llaine wrote:
 Any idea how can I call them ?
Just like any other function. Consider this: ---------- // i.d import std.stdio; extern(C) void hello() { writeln("hi from D"); } ---------- # d.rb require 'rubygems' require 'ffi' module DInterface extend FFI::Library ffi_lib './i.so' attach_function :rt_init, :rt_init, [], :int attach_function :rt_term, :rt_term, [], :int attach_function :hello, :hello, [], :void end # call init DInterface::rt_init # our function DInterface::hello # terminate DInterface::rt_term ---------- Compile... since I have 64 bit ruby on this computer, I had to build the 64 bit .so which is a pain: dmd -shared -m64 -fPIC -defaultlib=libphobos2.so i.d Now, run the ruby: ruby ./d.rb If you have the phobos shared lib installed system wide, that should work. If not, you'll get an error about not being able to find the libphobos2.so.whatever.version. In that case, just point the path: LD_LIBRARY_PATH=/path/to/dmd2/linux/lib64 ruby ./d.rb And boom, it runs. =============== Now, you might want to make it a bit more automatic for the Ruby user. I'd do that by calling rt_init in the include file and doing an at_exit for the term. Let's also pass a string. So here's the new files: --------- // i.d import std.stdio; import std.conv; extern(C) void hello(const char* name) { // remember, it is a C function, so use C string // and convert inside writeln("hi from D, ", to!string(name)); } -------- # d.rb require 'rubygems' require 'ffi' module DInterface extend FFI::Library ffi_lib './i.so' attach_function :rt_init, :rt_init, [], :int attach_function :rt_term, :rt_term, [], :int # now taking a string attach_function :hello, :hello, [:string], :void end # call init right here for the user DInterface::rt_init # terminate automatically at_exit do DInterface::rt_term end ---------- # user.rb # this is the user code require './d' DInterface::hello 'Ruby user!' =========== Compile dmd -shared -m64 -fPIC -defaultlib=libphobos2.so i.d and run LD_LIBRARY_PATH=~/d/dmd2/linux/lib64 ruby ./user.rb hi from D, Ruby user!
Aug 04 2016
next sibling parent reply llaine <darksioul6 gmail.com> writes:
On Thursday, 4 August 2016 at 12:14:48 UTC, Adam D. Ruppe wrote:
 On Thursday, 4 August 2016 at 10:36:05 UTC, llaine wrote:
 Any idea how can I call them ?
Just like any other function. Consider this: ---------- hi from D, Ruby user!
Wow thank you Adam, it's pretty impressive. I saw that your compiling using dmd with all thoses options. Can you explain me what is the benefit of using this ? I'm using just "dub" so far and its also working. Here is my repo https://github.com/llaine/ruby-dlang
Aug 04 2016
next sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
On Thursday, 4 August 2016 at 13:14:34 UTC, llaine wrote:
 I saw that your compiling using dmd with all thoses options. 
 Can you explain me what is the benefit of using this ?
very simple: avoiding dub. Adam is not using it.
Aug 04 2016
prev sibling next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 4 August 2016 at 13:14:34 UTC, llaine wrote:
 I saw that your compiling using dmd with all thoses options. 
 Can you explain me what is the benefit of using this ?
I just don't use dub, I don't see the benefit of it, but if it works for you, cool!
Aug 04 2016
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 4 August 2016 at 13:14:34 UTC, llaine wrote:
 Here is my repo https://github.com/llaine/ruby-dlang
FYI: to!string is kinda slow. If you want to do a fast blank, you should avoid conversions.... actually, I'm surprised it is faster than the native Ruby one at all.
Aug 05 2016
parent Adam D. Ruppe <destructionator gmail.com> writes:
A fun project might be to use D's reflection to automatically 
generate any C binding translators to a nice D interface and the 
Ruby file to access it.

I did something similar back in the day for PHP and JavaScript... 
it would be kinda tricky to do it with best efficiency, but 
getting it to work just conveniently would be fairly easy.
Aug 05 2016
prev sibling next sibling parent reply bachmeier <no spam.net> writes:
On Thursday, 4 August 2016 at 12:14:48 UTC, Adam D. Ruppe wrote:
 On Thursday, 4 August 2016 at 10:36:05 UTC, llaine wrote:
 Any idea how can I call them ?
Just like any other function. Consider this:
I'd like to put this example on the wiki unless you think there is a reason to not do so.
Aug 04 2016
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 4 August 2016 at 14:46:33 UTC, bachmeier wrote:
 I'd like to put this example on the wiki unless you think there 
 is a reason to not do so.
cool. I'll prolly slap it in this week in D soon too (I've been kinda short on material lately!)
Aug 04 2016
parent reply bachmeier <no spam.net> writes:
On Thursday, 4 August 2016 at 17:11:04 UTC, Adam D. Ruppe wrote:
 On Thursday, 4 August 2016 at 14:46:33 UTC, bachmeier wrote:
 I'd like to put this example on the wiki unless you think 
 there is a reason to not do so.
cool. I'll prolly slap it in this week in D soon too (I've been kinda short on material lately!)
https://wiki.dlang.org/Call_D_from_Ruby_using_FFI Please edit as desired.
Aug 04 2016
parent llaine <darksioul6 gmail.com> writes:
On Thursday, 4 August 2016 at 23:35:55 UTC, bachmeier wrote:
 On Thursday, 4 August 2016 at 17:11:04 UTC, Adam D. Ruppe wrote:
Happy that you guys put this on the Wiki !!
Aug 05 2016
prev sibling parent Martin Tschierschke <mt smartdolphin.de> writes:
On Thursday, 4 August 2016 at 12:14:48 UTC, Adam D. Ruppe wrote:
 On Thursday, 4 August 2016 at 10:36:05 UTC, llaine wrote:
 Any idea how can I call them ?
Just like any other function. Consider this:
[...]
 Compile
[...]
 hi from D, Ruby user!
Thank you a lot!!! I want to use some vibe.d rendering inside Ruby on Rails, this gives me a god starting point, for my experiments. Regards mt.
Aug 04 2016
prev sibling parent bachmeier <no spam.net> writes:
On Wednesday, 3 August 2016 at 15:56:34 UTC, llaine wrote:

 Okay on stack overflow, they are not using ffi but dl.
 I tried changing ffi to dl, it's the same don't work 
 unfortunatly.
This FFI example calls an init function from a library: https://github.com/ffi/ffi/blob/ce0e712bcb8876620e10c892d0b9005c84d92b53/samples/inotify.rb
Aug 03 2016
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 3 August 2016 at 13:44:46 UTC, llaine wrote:
   void puts(string str)
   {
     writeln(str);
   }
A D string isn't the same as a C string, so your params won't work as-is, and writeln requires the D runtime to be initialized. Does the ruby gem have a way to automatically call a particular setup and teardown function in the library? You could make init and and finalize functions that are called. Those run the Runtime.initialized and Runtime.finalize. Looking at their docs, I don't see an automatic one, but you could do a ruby constructor/destructor in the wrapper that does it for the end user. For the D string, either replace it with a C char* or try defining a struct on teh ruby side to match it... but C string is probably easiest.
Aug 03 2016
parent llaine <darksioul6 gmail.com> writes:
On Wednesday, 3 August 2016 at 14:02:12 UTC, Adam D. Ruppe wrote:
 On Wednesday, 3 August 2016 at 13:44:46 UTC, llaine wrote:
   void puts(string str)
   {
     writeln(str);
   }
A D string isn't the same as a C string, so your params won't work as-is, and writeln requires the D runtime to be initialized. Does the ruby gem have a way to automatically call a particular setup and teardown function in the library? You could make init and and finalize functions that are called. Those run the Runtime.initialized and Runtime.finalize. Looking at their docs, I don't see an automatic one, but you could do a ruby constructor/destructor in the wrapper that does it for the end user. For the D string, either replace it with a C char* or try defining a struct on teh ruby side to match it... but C string is probably easiest.
Okay so your advice is to directly replace string str by char* str and then call the runtime initialisation in my function?
Aug 03 2016