Successfully reported this slideshow.

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

0

Share

Loading in …3
×
1 of 35
1 of 35

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

0

Share

Download to read offline

Description

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

Transcript

  1. 1. C extensions easy in Ruby Apr 17th 2012 Muriel Salvan Open Source Lead developer and architect X-Aeon Solutions http://x-aeon.com
  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 coupled Ruby 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 your own C extensions easily
  8. 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. 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. 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
  11. 11. How to write your C extension
  12. 12. Default Ruby program structure myapp/ 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.rb require 'mkmf' create_makefile('myext') And that's 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.rb creating Makefile => ext/myapp/Makefile => make gcc -I. -I/usr/lib/ruby/1.8/i386-cygwin -I/usr/lib/ruby/1.8/i386-cygwin -I. -g -O2 -c myext.c gcc -shared -s -o myext.so myext.o -L. -L/usr/lib -L. -Wl,--enable-auto-image-base,--enable-auto-import,-- export-all -lruby -ldl -lcrypt => ext/myapp/myext.so
  17. 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
  18. 18. And now, package it in a nice Ruby gem!
  19. 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!
  20. 20. Platform dependent: gem spec myapp.compiled.gemspec.rb Gem::Specification.new do |spec| spec.name = 'my_app_compiled' spec.version = '0.1' spec.summary = 'Summary' spec.author = 'me' spec.bindir = 'bin' spec.executable = 'exec.rb' spec.files = [ 'bin/exec.rb', 'ext/myapp/myext.so' ] spec.platform = Gem::Platform::CURRENT spec.require_path = 'ext' end => 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.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. 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)
  23. 23. Platform independent: gem spec myapp.gemspec.rb Gem::Specification.new do |spec| spec.name = '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.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. 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
  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 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. 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. 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. 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!
  31. 31. Using external compiled libraries => FFI gem
  32. 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. 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. 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
  35. 35. Q/A

Description

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

Transcript

  1. 1. C extensions easy in Ruby Apr 17th 2012 Muriel Salvan Open Source Lead developer and architect X-Aeon Solutions http://x-aeon.com
  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 coupled Ruby 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 your own C extensions easily
  8. 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. 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. 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
  11. 11. How to write your C extension
  12. 12. Default Ruby program structure myapp/ 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.rb require 'mkmf' create_makefile('myext') And that's 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.rb creating Makefile => ext/myapp/Makefile => make gcc -I. -I/usr/lib/ruby/1.8/i386-cygwin -I/usr/lib/ruby/1.8/i386-cygwin -I. -g -O2 -c myext.c gcc -shared -s -o myext.so myext.o -L. -L/usr/lib -L. -Wl,--enable-auto-image-base,--enable-auto-import,-- export-all -lruby -ldl -lcrypt => ext/myapp/myext.so
  17. 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
  18. 18. And now, package it in a nice Ruby gem!
  19. 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!
  20. 20. Platform dependent: gem spec myapp.compiled.gemspec.rb Gem::Specification.new do |spec| spec.name = 'my_app_compiled' spec.version = '0.1' spec.summary = 'Summary' spec.author = 'me' spec.bindir = 'bin' spec.executable = 'exec.rb' spec.files = [ 'bin/exec.rb', 'ext/myapp/myext.so' ] spec.platform = Gem::Platform::CURRENT spec.require_path = 'ext' end => 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.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. 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)
  23. 23. Platform independent: gem spec myapp.gemspec.rb Gem::Specification.new do |spec| spec.name = '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.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. 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
  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 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. 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. 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. 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!
  31. 31. Using external compiled libraries => FFI gem
  32. 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. 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. 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
  35. 35. Q/A

More Related Content

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

×