Skip to content

Instantly share code, notes, and snippets.

@lomereiter
Created April 8, 2012 14:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lomereiter/2337729 to your computer and use it in GitHub Desktop.
Save lomereiter/2337729 to your computer and use it in GitHub Desktop.
howto: d & ruby ffi
test: test_c.o test_d.o
cc -shared -m32 test_c.o test_d.o -o test.so -lphobos2
test_c.o: test.c
gcc -c -m32 test.c -o test_c.o
test_d.o: test.d
dmd -c -m32 test.d -oftest_d.o
extern void attach(void);
extern void detach(void);
void __attach(void) __attribute__((constructor));
void __detach(void) __attribute__((destructor));
void __attach(void) { attach(); }
void __detach(void) { detach(); }
// the following two links are very useful:
// 1) http://stackoverflow.com/questions/9956994/return-a-class-instance-as-a-pointer-in-d
// 2) http://stackoverflow.com/questions/9759880/automatically-executed-functions-when-loading-shared-libraries
module test;
import std.conv;
import core.memory : GC;
import std.c.stdlib : malloc, free;
import core.runtime : Runtime;
extern (C) export void attach() { Runtime.initialize(); }
extern (C) export void detach() { Runtime.terminate(); }
public struct Foo {
public string hello = "hello, world!";
}
extern (C) size_t foo_size = Foo.sizeof;
import std.stdio : printf;
extern (C) void* foo_new() {
Foo* p = cast(Foo*)malloc(Foo.sizeof);
std.conv.emplace(p);
GC.addRange(p, Foo.sizeof);
return p;
}
extern (C) void foo_free(Foo* p) {
GC.removeRange(p);
free(cast(void*)p);
}
extern (C) immutable(char)* foo_hello(Foo* p) {
return p.hello.ptr;
}
void main() {}
#!/usr/bin/env ruby
require 'ffi'
module MyLibrary
extend FFI::Library
ffi_lib './test.so'
attach_function :foo_hello, [:pointer], :string
attach_function :foo_new, [], :pointer
attach_function :foo_free, [:pointer], :void
end
class Foo
def initialize
@ptr = MyLibrary.foo_new
# using FFI::AutoPointer causes too much overhead.
ObjectSpace.define_finalizer @ptr, Foo.finalize(@ptr)
end
def hello
@hello ||= MyLibrary.foo_hello @ptr
end
def self.finalize ptr
proc { MyLibrary.foo_free ptr }
end
end
require 'benchmark'
10000.times { Foo.new } # warming up
GC.start
# timings on my machine:
#
# 0.154811253 ruby 1.9.3p125 (2012-02-16 revision 34643) [i686-linux]
# 2.536235094 rubinius 1.2.4
# -- jruby 1.6.6 (ruby-1.8.7-p357) (2012-01-30 5673572) (Java HotSpot(TM) Client VM 1.7.0_03) [linux-i386-java]
#
# JRuby didn't manage to load D library:
# LoadError: Could not open library './test.so' : ./test.so: undefined symbol: __data_start
p Benchmark.realtime { 10000.times { Foo.new } }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment