Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Writing Ruby Extensions

5,697 views

Published on

A cursory overview of how to write Ruby extensions in C.

Does not cover more complicated topics such as compiling, wrapping C structs in Ruby structs, etc. It is instead meant to be an introduction to how to read and write Ruby in C as a first step of understanding the basics.

Published in: Technology, Education
  • Be the first to comment

Writing Ruby Extensions

  1. 1. Happy Independence Day
  2. 2. Ruby C Extensions Matt Todd
  3. 3. GeoIP
  4. 4. #include <GeoIP.h> GeoIP *gi; char *name = NULL; gi = GeoIP_open("GeoIPOrg.dat", GEOIP_MEMORY_CACHE); name = GeoIP_name_by_addr(gi, "24.24.24.24");
  5. 5. db = GeoIP::Organization.new("GeoIPOrg.dat", :memory) name = db.look_up('24.24.24.24')
  6. 6. Basics
  7. 7. // include the Ruby objects, data types, macros, and mechanisms #include "ruby.h" // define Ruby value variable VALUE HelloWorld = Qnil; // prototype the init function that defines the extension class void Init_hello_world(); // extension initialization function void Init_hello_world() { // class constant Ruby constant name parent class HelloWorld = rb_define_class("HelloWorld", rb_cObject); // Init other classes/modules }
  8. 8. // include the Ruby objects, data types, macros, and mechanisms #include "ruby.h" defines classes, keywords, values, functions, macros
  9. 9. // define Ruby value variable VALUE HelloWorld = Qnil; creates variable to hold class definition
  10. 10. // prototype the init function that defines the extension class void Init_hello_world(); prototype for init function
  11. 11. init function defines Ruby class interface and any methods // extension initialization function void Init_hello_world() { // class constant Ruby constant name parent class HelloWorld = rb_define_class("HelloWorld", rb_cObject); // Init other classes/modules } calls other init funcs
  12. 12. API
  13. 13. ReceiverClass = rb_define_class( "RubyClassName", rb_cParentClass); ReceiverModule = rb_define_module( "RubyModuleName")
  14. 14. SubReceiverClass = rb_define_class_under( ReceiverClass "SubRubyClassName", rb_cParentClass);
  15. 15. rb_define_method( ReceiverClass, "ruby_method_name", implementation_function, number_of_arguments);
  16. 16. implementation_function Returns a Ruby value the receiver VALUE implementation_function(VALUE self) { return Qnil; return nil }
  17. 17. number_of_arguments 0..17 => VALUE func(VALUE self, VALUE param1, ...); -1 => VALUE func(int argc, VALUE *args, VALUE self); -2 => VALUE func(VALUE self, VALUE *args);
  18. 18. rb_scan_args( int argc, VALUE *args, char *format, vars...);
  19. 19. format /(d)?(d)?(*)?(&)?/ number_of_mandatory_args number_of_optional_args splat? block? "1" => def meth(a) "01" => def meth(a = {}) "&" => def meth(&block) "11&" => def meth(a, b = {}, &block) "1*" => def meth(a, *rest)
  20. 20. usage rb_define_method(Foo, "bar", rb_foo_bar, -1); VALUE rb_foo_bar(int argc, VALUE *args, VALUE self) { VALUE a, b; rb_scan_args(argc, args, "11", &a, &b); // default to 10 if b not supplied if(NIL_P(b)) { b = 10; } return a * b; }
  21. 21. macros NIL_P(VALUE v); // #nil? // C => Ruby VALUE INT2NUM(int i); VALUE rb_str_new2(char *str); VALUE rb_float_new(double d); // Ruby => C int NUM2INT(VALUE i); double NUM2DBL(VALUE i); char* STR2CSTR(VALUE str);
  22. 22. Ruby types VALUE str = rb_str_new2("yo"); VALUE ary = rb_ary_new(); rb_ary_push(ary, VALUE a); VALUE b = rb_ary_pop(ary); VALUE hash = rb_hash_new(); rb_hash_aset(hash, VALUE key, VALUE a); rb_hash_aref(hash, VALUE key);
  23. 23. evaluation VALUE rb_eval_string( char *string); VALUE rb_funcall( VALUE receiver, ID rb_intern("meth"), int argc, VALUE *args);
  24. 24. Examples
  25. 25. // ActsAsFoo #include "ruby.h" VALUE rb_ActsAsFoo_foo_q(VALUE self) { return Qtrue; VALUE ClassesAndModulesSample = Qnil; } VALUE Foo = Qnil; VALUE rb_ActsAsFoo_included(VALUE self, VALUE target) { VALUE Foo_Base = Qnil; rb_extend_object(target, ActsAsFoo_ClassMethods); VALUE Bar = Qnil; return self; } VALUE ActsAsFoo = Qnil; VALUE ActsAsFoo_ClassMethods = Qnil; void Init_acts_as_foo() { ActsAsFoo = rb_define_module("ActsAsFoo"); void Init_classes_and_modules_sample(); rb_define_method(ActsAsFoo, "foo?", rb_ActsAsFoo_foo_q, 0); rb_define_singleton_method(ActsAsFoo, "included", rb_ActsAsFoo_included, 1); // Foo ActsAsFoo_ClassMethods = rb_define_module_under(ActsAsFoo, "ClassMethods"); VALUE rb_Foo_Base_find_b(VALUE self) { rb_define_method(ActsAsFoo_ClassMethods, "foo?", rb_ActsAsFoo_foo_q, 0); return Qtrue; } rb_include_module(Bar, ActsAsFoo); // call ActsAsFoo.included(Bar) manually since rb_include_module doesn't void Init_foo() { rb_funcall(ActsAsFoo, rb_intern("included"), 1, Bar); Foo = rb_define_class("Foo", rb_cObject); } Foo_Base = rb_define_class_under(Foo, "Base", rb_cObject); /////////////////////////////////////////////////////////////////////////////// rb_define_method(Foo_Base, "foo!", rb_Foo_Base_find_b, 0); } void Init_classes_and_modules_sample() { ClassesAndModulesSample = rb_define_class("ClassesAndModulesSample", // Bar rb_cObject); void Init_bar() { Init_foo(); Bar = rb_define_class("Bar", Foo_Base); Init_bar(); // class Bar < Foo::Base; end } Init_acts_as_foo(); }
  26. 26. class Foo class Base def foo! # deep, dark magic here true end end end
  27. 27. class Bar < Foo::Base; end @bar = Bar.new @bar.foo! #=> true
  28. 28. #include "ruby.h" VALUE ClassesAndModulesSample = Qnil; VALUE Foo = Qnil; VALUE Foo_Base = Qnil; VALUE Bar = Qnil; void Init_classes_and_modules_sample(); VALUE rb_Foo_Base_find_b(VALUE self) { return Qtrue; } void Init_foo() { Foo = rb_define_class("Foo", rb_cObject); Foo_Base = rb_define_class_under(Foo, "Base", rb_cObject); rb_define_method(Foo_Base, "foo!", rb_Foo_Base_find_b, 0); } void Init_bar() { Bar = rb_define_class("Bar", Foo_Base); } void Init_classes_and_modules_sample() { ClassesAndModulesSample = rb_define_class("ClassesAndModulesSample", rb_cObject); Init_foo(); Init_bar(); // class Bar < Foo::Base; end }
  29. 29. #include "ruby.h" VALUE ClassesAndModulesSample = Qnil; class Foo < Object VALUE Foo = Qnil; VALUE Foo_Base = Qnil; VALUE Bar = Qnil; void Init_classes_and_modules_sample(); VALUE rb_Foo_Base_find_b(VALUE self) { return Qtrue; } void Init_foo() { Foo = rb_define_class("Foo", rb_cObject); Foo_Base = rb_define_class_under(Foo, "Base", rb_cObject); rb_define_method(Foo_Base, "foo!", rb_Foo_Base_find_b, 0); } void Init_bar() { Bar = rb_define_class("Bar", Foo_Base); } void Init_classes_and_modules_sample() { ClassesAndModulesSample = rb_define_class("ClassesAndModulesSample", rb_cObject); Init_foo(); Init_bar(); // class Bar < Foo::Base; end }
  30. 30. class Foo < Object #include "ruby.h" VALUE ClassesAndModulesSample = Qnil; VALUE Foo = Qnil; class Base VALUE Foo_Base = Qnil; VALUE Bar = Qnil; void Init_classes_and_modules_sample(); VALUE rb_Foo_Base_find_b(VALUE self) { return Qtrue; } void Init_foo() { Foo = rb_define_class("Foo", rb_cObject); Foo_Base = rb_define_class_under(Foo, "Base", rb_cObject); rb_define_method(Foo_Base, "foo!", rb_Foo_Base_find_b, 0); } void Init_bar() { Bar = rb_define_class("Bar", Foo_Base); } void Init_classes_and_modules_sample() { ClassesAndModulesSample = rb_define_class("ClassesAndModulesSample", rb_cObject); Init_foo(); Init_bar(); // class Bar < Foo::Base; end }
  31. 31. #include "ruby.h" class Foo < Object VALUE ClassesAndModulesSample = Qnil; class Base VALUE Foo = Qnil; VALUE Foo_Base = Qnil; VALUE Bar = Qnil; def foo! void Init_classes_and_modules_sample(); VALUE rb_Foo_Base_find_b(VALUE self) { return Qtrue; } void Init_foo() { Foo = rb_define_class("Foo", rb_cObject); Foo_Base = rb_define_class_under(Foo, "Base", rb_cObject); rb_define_method(Foo_Base, "foo!", rb_Foo_Base_find_b, 0); } void Init_bar() { Bar = rb_define_class("Bar", Foo_Base); } void Init_classes_and_modules_sample() { ClassesAndModulesSample = rb_define_class("ClassesAndModulesSample", rb_cObject); Init_foo(); Init_bar(); // class Bar < Foo::Base; end }
  32. 32. #include "ruby.h" VALUE ClassesAndModulesSample = Qnil; VALUE Foo = Qnil; VALUE Foo_Base = Qnil; VALUE Bar = Qnil; void Init_classes_and_modules_sample(); VALUE rb_Foo_Base_find_b(VALUE self) { return Qtrue; } void Init_foo() { Foo = rb_define_class("Foo", rb_cObject); Foo_Base = rb_define_class_under(Foo, "Base", rb_cObject); rb_define_method(Foo_Base, "foo!", rb_Foo_Base_find_b, 0); } void Init_bar() { Bar = rb_define_class("Bar", Foo_Base); } void Init_classes_and_modules_sample() { ClassesAndModulesSample = rb_define_class("ClassesAndModulesSample", rb_cObject); Init_foo(); Init_bar(); // class Bar < Foo::Base; end }
  33. 33. module ActsAsFoo def foo? true end def included(target) target.extend ClassMethods end module ClassMethods def foo? true end end end
  34. 34. class Bar < Foo include ActsAsFoo end Bar.foo? #=> true @bar = Bar.new @bar.foo? #=> true
  35. 35. #include "ruby.h" VALUE ActsAsFoo = Qnil; VALUE ActsAsFoo_ClassMethods = Qnil; VALUE rb_ActsAsFoo_foo_q(VALUE self) { return Qtrue; } VALUE rb_ActsAsFoo_included(VALUE self, VALUE target) { rb_extend_object(target, ActsAsFoo_ClassMethods); return self; } void Init_acts_as_foo() { ActsAsFoo = rb_define_module("ActsAsFoo"); rb_define_method(ActsAsFoo, "foo?", rb_ActsAsFoo_foo_q, 0); rb_define_singleton_method(ActsAsFoo, "included", rb_ActsAsFoo_included, 1); ActsAsFoo_ClassMethods = rb_define_module_under(ActsAsFoo, "ClassMethods"); rb_define_method(ActsAsFoo_ClassMethods, "foo?", rb_ActsAsFoo_foo_q, 0); }
  36. 36. #include "ruby.h" VALUE ActsAsFoo = Qnil; VALUE ActsAsFoo_ClassMethods = Qnil; VALUE rb_ActsAsFoo_foo_q(VALUE self) { return Qtrue; } VALUE rb_ActsAsFoo_included(VALUE self, VALUE target) { rb_extend_object(target, ActsAsFoo_ClassMethods); return self; } void Init_acts_as_foo() { ActsAsFoo = rb_define_module("ActsAsFoo"); rb_define_method(ActsAsFoo, "foo?", rb_ActsAsFoo_foo_q, 0); rb_define_singleton_method(ActsAsFoo, "included", rb_ActsAsFoo_included, 1); ActsAsFoo_ClassMethods = rb_define_module_under(ActsAsFoo, "ClassMethods"); rb_define_method(ActsAsFoo_ClassMethods, "foo?", rb_ActsAsFoo_foo_q, 0); }
  37. 37. rb_include_module(Bar, ActsAsFoo); // call ActsAsFoo.included(Bar) manually since rb_include_module doesn't rb_funcall(ActsAsFoo, rb_intern("included"), 1, Bar);
  38. 38. Fin.
  39. 39. http://github.com/mtodd/ruby-c
  40. 40. Resources http://rubycentral.com/pickaxe/ext_ruby.html

×