Call D from Ruby using FFI

From D Wiki
Revision as of 13:24, 3 March 2018 by D user (talk | contribs) (Category:HowTo)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Adam Ruppe posted this example in the Learn Forum. The goal is to write a function in D and call it from Ruby using the Ruby-FFI extension.

Shorter Version

Create the file "i.d" containing

import std.stdio;

extern(C)
void hello()
{
	writeln("hi from D");
}

Compile it as a shared library. For example, to compile as a 64-bit shared library on Linux, you can do

dmd -shared -m64 -fPIC -defaultlib=libphobos2.so i.d

Create the file "d.rb" containing

 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

Run the Ruby file:

ruby ./d.rb

You should see

hi from D

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

More Convenient Version

You might want to make it a bit more automatic for the Ruby user. You can do that by calling rt_init in the include file and doing an at_exit for the term.

Create "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));
}

Create "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

Create "user.rb":

 require './d'
 
 DInterface::hello 'Ruby user!'

Compile and run (Linux 64-bit version):

dmd -shared -m64 -fPIC -defaultlib=libphobos2.so i.d
LD_LIBRARY_PATH=~/d/dmd2/linux/lib64 ruby ./user.rb