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.

Ruby C extensions at the Ruby drink-up of Sophia, April 2012

Presented at the Ruby Drink-up of Sophia Antipolis on the 17th of April 2012 by Muriel Salvan (@MurielSalvan).

  • Be the first to comment

  • Be the first to like this

Ruby C extensions at the Ruby drink-up of Sophia, April 2012

  1. 1. C extensions easy in Ruby Apr 17th 2012 Muriel Salvan Open Source Lead developer and architect X-Aeon Solutions
  2. 2. Why Ruby and C ?
  3. 3. Ruby without C
  4. 4. C without Ruby
  5. 5. Ruby and C combined
  6. 6. Ruby and C are already coupledRuby core: around 100 classes written in C (String, Rational, Marshal, IO...)Ruby standard libs: 35 libs written in C: (BigDecimal, Date, OpenSSL...)
  7. 7. Now You can write yourown C extensions easily
  8. 8. What is a C extension ?A requirable library from Ruby code ... require myext… that can use any other Rubys object or library,… written in C,… and compiled.
  9. 9. Technically speakingA C extension is a compiled library (.so or .dll) that:defines 1 specific C function,is accessible in Rubys load path (same as other .rb files)It is used the same way Rubys libraries (.rb) are (packaging, search path, require...)
  10. 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 WindowsLittle C knowledgeSome Rubys C API knowledge
  11. 11. How to write your C extension
  12. 12. Default Ruby program structuremyapp/ bin/ exec.rb lib/ myapp/ mylib.rb ext/ myapp/ myext.c extconf.rb myotherlib/ otherlib.c extconf.rb
  13. 13. Write the C file ext/myapp/myext.c#include "ruby.h"void Init_myext() { printf("Hello Ruby from C!n");}
  14. 14. Write the extconf file ext/myapp/extconf.rbrequire mkmfcreate_makefile(myext)And thats it!Your C extension is ready to be compiled and used
  15. 15. How to compile and use your C extension
  16. 16. Compile it in ext/myapp/=> ruby extconf.rbcreating Makefile => ext/myapp/Makefile=> makegcc -I. -I/usr/lib/ruby/1.8/i386-cygwin -I/usr/lib/ruby/1.8/i386-cygwin -I. -g -O2 -c myext.cgcc -shared -s -o myext.o -L. -L/usr/lib -L. -Wl,--enable-auto-image-base,--enable-auto-import,-- export-all -lruby -ldl -lcrypt => ext/myapp/
  17. 17. Use it bin/exec.rb#!/bin/env rubyputs Before requiring C extensionrequire myapp/myextputs After requiring C extension=> ruby -Iext bin/exec.rbBefore requiring C extensionHello Ruby from C!After requiring C extension
  18. 18. And now, package it in a nice Ruby gem!
  19. 19. First flavor: Package the compiled extensionAdd the compiled extension to the files list (like any other library)Add your ext/ directory as a required pathDont forget to set your Gem platform as specific!
  20. 20. Platform dependent: gem spec do |spec| = my_app_compiled spec.version = 0.1 spec.summary = Summary = me spec.bindir = bin spec.executable = exec.rb spec.files = [ bin/exec.rb, ext/myapp/ ] spec.platform = Gem::Platform::CURRENT spec.require_path = extend=> gem build myapp.compiled.gemspec.rb Successfully built RubyGem Name: my_app_compiled Version: 0.1 File: my_app_compiled-0.1-x86-cygwin.gem
  21. 21. Platform dependent: Install and run it=> gem install my_app_compiled-0.1-x86- cygwin.gemSuccessfully installed my_app_compiled-0.1- x86-cygwin1 gem installedInstalling ri documentation for my_app_compiled-0.1-x86-cygwin...Installing RDoc documentation for my_app_compiled-0.1-x86-cygwin...=> exec.rbBefore requiring C extensionHello Ruby from C!After requiring C extension
  22. 22. Second flavor: Platform independent packagingAdd the C extension source files to the files listAdd your ext/ directory as a required pathKeep your Gem platform as RubyRegister the C extension (path to the extconf.rb file)
  23. 23. Platform independent: gem spec do |spec| = my_app # { ... } spec.executable = exec.rb spec.files = [ bin/exec.rb, ext/myapp/myext.c, ext/myapp/extconf.rb ] spec.platform = Gem::Platform::RUBY spec.require_path = ext spec.extensions = [ ext/myapp/extconf.rb ]end gem build myapp.gemspec.rb=> Successfully built RubyGem Name: my_app Version: 0.1 File: my_app-0.1.gem
  24. 24. Platform independent: Install and run it=> gem install my_app-0.1.gemBuilding native extensions. This could take a while...Successfully installed my_app-0.11 gem installedInstalling ri documentation for my_app-0.1...Installing RDoc documentation for my_app-0.1...=> exec.rbBefore requiring C extensionHello Ruby from C!After requiring C extension
  25. 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 toUsers do not need install it any development environment
  26. 26. Need more than a Hello World ? => The Ruby C API
  27. 27. module MyModule class MyClass def my_method(param1, param2, param3) end end endstatic 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. 28. if param1 == nil puts Param1 is nil return nil else return param1 + 42 endif (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. 29. param2.each do |elem| elemstr = elem.to_s elemstr[0] = A puts elemstr[0..3] endint 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. 30. param3.block_method(3) do | block_param| puts param1 + block_param endstatic 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!
  31. 31. Using external compiled libraries => FFI gem
  32. 32. FFI gem Import external functions from acompiled 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. 33. FFI featuresIt has a very intuitive DSLIt supports all C native typesIt supports C structs (also nested), enums and global variablesIt supports callbacksIt has smart methods to handle memory management of pointers and structs
  34. 34. Links Makefile generation options: Linuxtopia tutorial mkmf rdoc Ruby C API: Eqqon article Matz Readme Metaprogramming FFI gemThis presentation is available under CC-BY license by Muriel Salvan
  35. 35. Q/A