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

Building C Extensions in Ruby

Building C Extensions in Ruby

A starting guide to your first C extension.

André Medeiros

February 21, 2014
Tweet

More Decks by André Medeiros

Other Decks in Programming

Transcript

  1. C types as first class citizens Great for gems that

    involve back-and-forth communication (RMagick, Nokogiri)
  2. Add the Rake compile task # speedy_gem.gemspec! spec.add_development_dependency "rake-compiler"! !

    # Rakefile! require "rake/extensiontask"! ! Rake::ExtensionTask.new("speedy_gem") do |ext|! extension.lib_dir = "lib/speedy_gem"! end!
  3. initializing your extension // ext/speedy_gem/speedy_gem.c! ! #include "ruby.h"! ! VALUE

    SpeedyGemClass = Qnil;! ! void Init_rubyconf() {! // Initialization code goes here! }!
  4. Defining your first class // ext/speedy_gem/speedy_gem.c! ! #include "ruby.h"! !

    VALUE SpeedyGemClass = Qnil;! ! void Init_rubyconf() {! SpeedyGemClass = rb_define_class("SpeedyGem", rb_cObject);! }!
  5. Memory management // ext/speedy_gem/speedy_gem.c! ! #include "ruby.h"! ! VALUE SpeedyGemClass

    = Qnil;! ! void Init_rubyconf() {! SpeedyGemClass = rb_define_class("SpeedyGem", rb_cObject);! rb_define_alloc_func(SpeedyGemClass, rb_alloc);! }! ! VALUE rb_alloc(VALUE self) {! char *string = calloc(1024, sizeof(char));! return Data_Wrap_Struct(SpeedyGemClass, NULL, rb_free, string);! }! ! void rb_free(char *string) {! free(string);! }!
  6. Getting the C struct // ext/speedy_gem/speedy_gem.c! ! #include "ruby.h"! !

    VALUE SpeedyGemClass = Qnil;! ! void Init_rubyconf() {! SpeedyGemClass = rb_define_class("SpeedyGem", rb_cObject);! rb_define_method(SpeedyGemClass, "string", rb_get_string, 0);! }! ! VALUE rb_get_string(VALUE self) {! char *string;! Data_Get_Struct(self, char, string);! return rb_str_new2(string);! }!
  7. your first method // ext/speedy_gem/speedy_gem.c! ! #include "ruby.h"! ! VALUE

    SpeedyGemClass = Qnil;! ! void Init_rubyconf() {! SpeedyGemClass = rb_define_class("SpeedyGem", rb_cObject);! rb_define_method(SpeedyGemClass, "say_hi", rb_say_hi, 1);! }! ! VALUE rb_say_hi(VALUE self, VALUE name) {! VALUE phrase = rb_str_new2("Hello, ");! rb_str_append(phrase, name);! return phrase;! } !
  8. built-in types // Checks if radius is a Float! Check_Type(radius,

    T_FLOAT);! ! // Checks if age is a Fixnum! Check_Type(age, T_FIXNUM);! ! // Checks if name is a String! Check_Type(name, T_STRING);! ! // Checks if options is a Hash! Check_Type(options, T_HASH); !
  9. Fixed number // ext/speedy_gem/speedy_gem.c! ! #include "ruby.h"! ! VALUE SpeedyGemClass

    = Qnil;! ! void Init_rubyconf() {! SpeedyGemClass = rb_define_class("SpeedyGem", rb_cObject);! rb_define_method(SpeedyGemClass, "say_hi", rb_say_hi, 1);! }! ! VALUE rb_say_hi(VALUE self, VALUE name) {! VALUE phrase = rb_str_new2("Hello, ");! rb_str_append(phrase, name);! return phrase;! } !
  10. optional // ext/speedy_gem/speedy_gem.c! ! #include "ruby.h"! ! VALUE SpeedyGemClass =

    Qnil;! ! void Init_rubyconf() {! SpeedyGemClass = rb_define_class("SpeedyGem", rb_cObject);! rb_define_method(SpeedyGemClass, "say_hi", rb_say_hi, -1);! }! ! VALUE rb_say_hi(int argc, VALUE *argv, VALUE self) {! VALUE phrase = rb_str_new2("Hello, ");! VALUE name;! rb_scan_args(argc, argv, "01", &name);! ! if(!RTEST(name))! name = rb_str_new2("World");! ! rb_str_append(phrase, name);! return phrase;! } !
  11. Strings // Create a Ruby string from char *! VALUE

    phrase = rb_str_new2("Hello, ");! ! // Append two Ruby strings together! rb_str_append(phrase, subject);! ! // Append a char * to a Ruby string! rb_str_cat2(phrase, "Something");! ! // Duplicate a Ruby string! VALUE copy = rb_str_dup(phrase);! ! // Get a substring from a Ruby string! VALUE without_hello = rb_str_substr(phrase, 7, 100);!
  12. Arrays // Create a Ruby array! VALUE array = rb_ary_new();!

    ! // Push values to a Ruby array! rb_ary_push(array, rb_str_new2("One thing"));! rb_ary_push(array, rb_str_new2("Another thing"));! ! // Add a value to a specific position in a Ruby array! rb_ary_store(array, 2, rb_str_new2("A third thing"));! ! // Reverse a Ruby array! VALUE reversed = rb_ary_reverse(array);! ! // Create a Ruby string out of a Ruby array! VALUE string = rb_ary_join(array, rb_str_new2(", "));!
  13. Hashes // Creating a Ruby hash! VALUE hash = rb_hash_new();!

    ! // Adding values! rb_hash_aset(! hash, // The Ruby hash! ID2SYM(rb_intern("universal_answer")), // Key! INT2NUM(42) // Value! );! ! // Getting a value from a Ruby hash! VALUE answer = rb_hash_aref(hash, ID2SYM(rb_intern("universal_answer")));!
  14. Blocks // Check if a block was given and yield

    a value if so.! if(rb_block_given_p()) rb_yield(value)! ! // Same as above, but multiple values! if(rb_block_given_p()) rb_yield_values(value1, value2)! ! // Store a block into a variable and call it later.! VALUE block = rb_block_proc();! rb_funcall(block, rb_intern("call"), 2, value1, value2);!