Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Extending Ruby

Extending Ruby

A small introduction to development C extensions for Ruby

Raúl Naveiras

April 27, 2012
Tweet

More Decks by Raúl Naveiras

Other Decks in Programming

Transcript

  1. // filename madridrb.c #include <ruby.h>; static int id_push; static VALUE

    t_init(VALUE self) { VALUE people; people = rb_ary_new(); rb_iv_set(self, "@people", people); return self; } static VALUE t_add(VALUE self, VALUE obj) { VALUE people; people = rb_iv_get(self, "@people"); rb_funcall(people, id_push, 1, obj); return people; } VALUE cMadridrb; void Init_madridrb() { cMadridrb = rb_define_class("Madridrb", rb_cObject); rb_define_method(cMadridrb, "initialize", t_init, 0); rb_define_method(cMadridrb, "add", t_add, 1); id_push = rb_intern("push"); }
  2. class Madridrb def initialize @people = [] end def add(person)

    @people << person end end # filename extconf.rb require 'mkmf' dir_config('madridrb') create_makefile('madridrb')
  3. RUBY OBJECTS IN C EVERYTHING IN RUBY IS AN OBJECT

    WHEN WRITING RUBY IN C, EVERYTHING IS A VALUE
  4. CHECK THE DATA TYPE OF ‘VALUE’ switch (TYPE(obj)) { case

    T_FIXNUM: // process Fixnum break; case T_STRING: // process String break; case T_ARRAY: // process Array break; default: // raise exception rb_raise(rb_eTypeError, "not valid value"); break; } Check_Type(str, T_STRING); FIXNUM_P(value); // fixnum? NIL_P(value); // nil?
  5. T_NIL T_OBJECT T_CLASS T_MODULE T_FLOAT! T_STRING T_REGEXP T_ARRAY! T_HASH! !

    T_STRUCT! T_BIGNUM! T_FIXNUM! T_COMPLEX T_RATIONAL T_FILE! ! T_TRUE! ! T_FALSE T_DATA T_SYMBOL nil ordinary object class module floating point number string regular expression array associative array (Ruby) structure multi precision integer Fixnum(31bit or 63bit integer) complex number rational number IO true false data symbol CHECK THE DATA TYPE OF ‘VALUE’
  6. CHECK THE DATA TYPE OF THE ‘VALUE’ #include <ruby.h>; static

    VALUE print_string(VALUE self, VALUE string) { Check_Type(string, T_STRING); printf("%s", RSTRING(string)->ptr); return Qnil; }
  7. CONVERT A VALUE INTO C DATA // converts any Ruby

    numbers into C integers int NUM2INT(Numeric); int FIX2INT(Fixnum); unsigned int NUM2UINT(Numeric); unsigned int FIX2UINT(Fixnum); long NUM2LONG(Number); long FIX2LONG(Fixnum); double NUM2DBL(Numeric); // convert any Ruby strings into C char* char NUM2ULONG(Numeric_or_String); char* StringValue(str); char* StringValuePtr(str); char* StringValueCStr(str); // for other objects RSTRING_LEN(obj); // length of the Ruby string RSTRING_PTR(obj); // pointer to string storage VALUE array; RARRAY(array)->len; // length of the Ruby array RARRAY_LEN RARRAY(array)->capa; // capacity of the Ruby array RARRAY(array)->ptr; // pointer to array storage RARRAY_PTR // There are similar accessors for hashes (RHASH), files (RFILE) and so on.
  8. CONVERT C DATA INTO A VALUE // convert C numbers

    to Ruby values INT2FIX(); // for integers within 31bits. INT2NUM(); // for arbitrary sized integer.
  9. CONVERT C DATA INTO A VALUE // convert C numbers

    to Ruby values INT2FIX(); // for integers within 31bits. INT2NUM(); // for arbitrary sized integer. int version = 100; static VALUE madridrb_version(VALUE self) { return INT2NUM(version); }
  10. STRINGS rb_str_new(const char *ptr, long len); // Creates a new

    Ruby string. rb_str_new2(const char *ptr); // Creates a new Ruby string from a C string. rb_str_new_cstr(const char *ptr); // This is equivalent to rb_str_new(ptr, strlen(ptr)). rb_sprintf(const char *format, ...); rb_vsprintf(const char *format, va_list ap); rb_str_cat(VALUE str, const char *ptr, long len); rb_str_cat2(VALUE str, const char* ptr); rb_str_catf(VALUE str, const char* format, ...); rb_str_vcatf(VALUE str, const char* format, va_list ap); rb_enc_str_new(const char *ptr, long len, rb_encoding *enc); rb_usascii_str_new(const char *ptr, long len); rb_usascii_str_new_cstr(const char *ptr); rb_str_resize(VALUE str, long len); rb_str_set_len(VALUE str, long len);
  11. ARRAYS // Array functions rb_ary_new(); rb_ary_new2(long len); rb_ary_new3(long n, ...);

    rb_ary_new4(long n, VALUE *elts); //Creates an n-element array from a C array. rb_ary_to_ary(VALUE obj); rb_ary_aref(argc, VALUE *argv, VALUE ary); rb_ary_entry(VALUE ary, long offset); // ary[offset] rb_ary_subseq(VALUE ary, long beg, long len); // ary[beg,len] rb_ary_push(VALUE ary, VALUE val); rb_ary_pop(VALUE ary); rb_ary_shift(VALUE ary); rb_ary_unshift(VALUE ary, VALUE val); rb_ary_cat(VALUE ary, const VALUE *ptr, long len);
  12. MODULES, CLASSES, METHODS... VALUE rb_define_module(const char *name); VALUE rb_define_class(const char

    *name, VALUE super); VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super); VALUE rb_define_module_under(VALUE outer, const char *name); void rb_define_method(VALUE klass, const char *name, VALUE (*func)(), int argc); void rb_define_singleton_method(VALUE object, const char *name, VALUE (*func)(), int argc); void rb_define_private_method(VALUE klass, const char *name, VALUE (*func)(), int argc); void rb_define_protected_method(VALUE klass, const char *name, VALUE (*func)(), int argc); void rb_define_const(VALUE klass, const char *name, VALUE val); void rb_define_global_const(const char *name, VALUE val); void rb_include_module(VALUE klass, VALUE val); void rb_extend_object(VALUE object, VALUE val);
  13. INVOKING RUBY METHODS FROM C VALUE rb_funcall(VALUE recv, ID mid,

    int nargc, ...); VALUE rb_funcall2(VALUE recv, ID mid, int argc, VALUE *argv);
  14. ACCESSING RUBY VARIABLES AND CONSTANTS VALUE rb_ivar_get(VALUE obj, ID id);

    VALUE rb_ivar_set(VALUE obj, ID id, VALUE val); VALUE rb_const_get(VALUE obj, ID id);
  15. // filename madridrb.c #include <ruby.h>; static int id_push; static VALUE

    t_init(VALUE self) { VALUE people; people = rb_ary_new(); rb_iv_set(self, "@people", people); return self; } static VALUE t_add(VALUE self, VALUE obj) { VALUE people; people = rb_iv_get(self, "@people"); rb_funcall(people, id_push, 1, obj); return people; } VALUE cMadridrb; void Init_madridrb() { cMadridrb = rb_define_class("Madridrb", rb_cObject); rb_define_method(cMadridrb, "initialize", t_init, 0); rb_define_method(cMadridrb, "add", t_add, 1); id_push = rb_intern("push"); }
  16. ALLOCATING FUNCTIONS #include <ruby.h>; static VALUE rb_alloc(VALUE klass) { VALUE

    obj; // malloc and other stuff obj = Data_Wrap_Struct(klass, .....) return obj; } VALUE cMadridrb; static Init_madridrb(VALUE self, VALUE wadus) { cMadridrb = rb_define_class("Madridrb", rb_cObject); rb_define_alloc_func(cMadridrb, rb_alloc); // ... }
  17. • DOESN’T NEED COMPILATION • MULTI-PLATFORM • MULTI-IMPLEMENTATION • THE

    CODE IS RUBY. EASY TO READ AND EASY TO WRITE RUBY FFI
  18. module Fibonacci extend FFI::Library ffi_lib './fibonacci.so' attach_function :rfibonnaci, [:int], :int

    attach_function :ifibonnaci, [:int], :int end require 'ffi' require 'fibonacci' puts Fibonacci.rfibonnaci 10 puts Fibonacci.ifibonnaci 40