Calling D from Ruby: There and Back Again
by Paweł Świątkowski
30 Apr 2015
When I was writing previous post about using FFI to “outsource” some work from Ruby to D, I was pretty sure that this is only one-way option: you call a D function with some parameters and obtain final result. It turns out, though, that FFI is much more awesome than I thought and you can use callbacks to enhance coöperation between those two. Using that I extended my prime example so now it prints every non-prime number met alongway too.
First of all, we have to change our D code:
import std.stdio, std.math;
extern(C) {
alias CallbackFunc = void function(long);
long firstPrime(long bottom, CallbackFunc callback) {
auto candidate = bottom;
while(true) {
candidate += 1;
if (candidate % 2 == 0) {
callback(candidate);
continue;
}
auto should_return = true;
for(long i = 3; i<sqrt(real(candidate)); i += 2) {
if (candidate % i == 0) {
should_return = false;
break;
}
}
if(should_return)
return candidate;
else
callback(candidate);
}
}
}
A second argument is added to firstPrime
function. It is any function that accepts one long
and returns nothing. Then we call it in every place where we are sure that our current candidate for a prime is actually not a prime with simple callback(candidate)
.
Now, to the Ruby side:
module DPrime
extend FFI::Library
ffi_lib './prime.so'
callback :nonPrimeCallback, [ :long ], :void
attach_function :firstPrime, [ :long, :nonPrimeCallback ], :long
end
This is how we register callbacks. Not really much of a magic here, but now we can call
puts DPrime.firstPrime(11001503, lambda {|np| puts "#{np} is not a prime!"})
And the result is:
11001504 is not a prime!
11001505 is not a prime!
11001506 is not a prime!
11001507 is not a prime!
11001508 is not a prime!
11001509 is not a prime!
11001510 is not a prime!
11001511 is not a prime!
11001512 is not a prime!
11001513 is not a prime!
11001514 is not a prime!
11001515 is not a prime!
11001516 is not a prime!
11001517
All this is getting more and more interesting for me. D seems to be quite powerful (although right now probably loses to Rust in terms of popularity), multiplatform and relatively easy to combine with Ruby. It might be really useful when your Ruby application gets too slow because of many computations and you don’t feel like writing cumbersome C code.