Calling D from Ruby: There and Back Again

This is a followup to previous blog post. You should probably read it first.

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.

Related posts