6. Ruby and C are
already coupled
Ruby core: around 100 classes written in
C (String, Rational, Marshal, IO...)
Ruby standard libs: 35 libs written in C:
(BigDecimal, Date, OpenSSL...)
8. What is a C extension ?
A requirable library from Ruby code ...
require 'myext'
… that can use any other Ruby's object
or library,
… written in C,
… and compiled.
9. Technically speaking
A C extension is a compiled library (.so
or .dll) that:
defines 1 specific C function,
is accessible in Ruby's load path (same
as other .rb files)
It is used the same way Ruby's libraries
(.rb) are (packaging, search path,
require...)
10. What do you need to
write C extensions ?
A C development environment ([g]cc, ld,
[g]make...)
Already installed on *nix
Cygwin, MinGW, Ruby DevKit on Windows
Little C knowledge
Some Ruby's C API knowledge
13. Write the C file
ext/myapp/myext.c
#include "ruby.h"
void Init_myext() {
printf("Hello Ruby from C!n");
}
14. Write the extconf file
ext/myapp/extconf.rb
require 'mkmf'
create_makefile('myext')
And that's it!
Your C extension is ready to be compiled
and used
17. Use it
bin/exec.rb
#!/bin/env ruby
puts 'Before requiring C extension'
require 'myapp/myext'
puts 'After requiring C extension'
=> ruby -Iext bin/exec.rb
Before requiring C extension
Hello Ruby from C!
After requiring C extension
19. First flavor:
Package the compiled
extension
Add the compiled extension to the files
list (like any other library)
Add your ext/ directory as a required
path
Don't forget to set your Gem platform as
specific!
21. Platform dependent:
Install and run it
=> gem install my_app_compiled-0.1-x86-
cygwin.gem
Successfully installed my_app_compiled-0.1-
x86-cygwin
1 gem installed
Installing ri documentation for
my_app_compiled-0.1-x86-cygwin...
Installing RDoc documentation for
my_app_compiled-0.1-x86-cygwin...
=> exec.rb
Before requiring C extension
Hello Ruby from C!
After requiring C extension
22. Second flavor:
Platform independent
packaging
Add the C extension source files to the
files list
Add your ext/ directory as a required
path
Keep your Gem platform as Ruby
Register the C extension (path to the
extconf.rb file)
24. Platform independent:
Install and run it
=> gem install my_app-0.1.gem
Building native extensions. This could take
a while...
Successfully installed my_app-0.1
1 gem installed
Installing ri documentation for my_app-0.1...
Installing RDoc documentation for
my_app-0.1...
=> exec.rb
Before requiring C extension
Hello Ruby from C!
After requiring C extension
25. Which flavor the best ?
Platform dependent: Platform independent:
Need to release 1 Need to release just 1
Ruby gem per Ruby gem
platform (need to Users must have a C
compile on each development
platform) environment to
Users do not need install it
any development
environment
27. module MyModule
class MyClass
def my_method(param1, param2,
param3)
end
end
end
static VALUE myclass_mymethod(
VALUE rb_self,
VALUE rb_param1,
VALUE rb_param2,
VALUE rb_param3) {
}
void Init_myext() {
VALUE mymodule = rb_define_module("MyModule");
VALUE myclass = rb_define_class_under(mymodule,
"MyClass", rb_cObject);
rb_define_method(myclass, "my_method",
myclass_mymethod, 3);
}
28. if param1 == nil
puts 'Param1 is nil'
return nil
else
return param1 + 42
end
if (rb_param1 == Qnil) {
rb_funcall(rb_self, rb_intern("puts"), 1,
rb_str_new2("Param1 is nil"));
return Qnil;
} else {
int param1 = FIX2INT(rb_param1);
VALUE result = INT2FIX(param1 + 42);
return result;
}
29. param2.each do |elem|
elemstr = elem.to_s
elemstr[0] = 'A'
puts elemstr[0..3]
end
int nbrelems = RARRAY(rb_param2)->len;
int idx;
for (idx = 0; idx < nbrelems; ++idx) {
VALUE rb_elem = rb_ary_entry(rb_param2, idx);
VALUE rb_elemstr = rb_funcall(rb_elem,
rb_intern("to_s"), 0);
char* elemstr = RSTRING_PTR(rb_elemstr);
elemstr[0] = 'A';
char* substr = (char*)malloc(5);
strncpy(substr, elemstr, 4);
substr[4] = '0';
rb_funcall(rb_self, rb_intern("puts"), 1,
rb_str_new2(substr));
free(substr);
}
30. param3.block_method(3) do |
block_param|
puts param1 + block_param
end
static VALUE call_block_method(VALUE rb_params) {
VALUE rb_object = rb_ary_entry(rb_params, 0);
VALUE rb_value = rb_ary_entry(rb_params, 1);
return rb_funcall(rb_object, rb_intern("block_method"), 1, rb_value);
}
static VALUE yielded_block(VALUE rb_yield_params, VALUE
rb_iterate_params) {
VALUE rb_block_param = rb_yield_params;
VALUE rb_self = rb_ary_entry(rb_iterate_params, 0);
VALUE rb_param1 = rb_ary_entry(rb_iterate_params, 1);
return rb_funcall(rb_self, rb_intern("puts"), 1,
INT2FIX(FIX2INT(rb_block_param)+FIX2INT(rb_param1)));
}
rb_iterate(
call_block_method,
rb_ary_new3(2, rb_param3, INT2FIX(3)),
yielded_block,
rb_ary_new3(2, rb_self, rb_param1)
);
Thanks Matz for Ruby!
32. FFI gem
Import external functions from a
compiled library into a Ruby module
require 'ffi'
module MyLib
extend FFI::Library
ffi_lib 'c'
attach_function :puts, [ :string ], :int
end
MyLib.puts 'Hello, World using libc!'
33. FFI features
It has a very intuitive DSL
It supports all C native types
It supports C structs (also nested),
enums and global variables
It supports callbacks
It has smart methods to handle memory
management of pointers and structs
34. Links
Makefile generation options:
Linuxtopia tutorial
mkmf rdoc
Ruby C API:
Eqqon article
Matz' Readme
Metaprogramming
FFI gem
This presentation is available under CC-BY license by Muriel Salvan