
Going Native Frederick Cheung CTO, dressipi.com @fglc2 / spacevatican.org Monday, 10 December 12 Why? • Performance (bottleneck) • Platform specific functionality • Access to best in class libraries Monday, 10 December 12 Downsides • Generally harder • Memory management • ruby c api poorly documented • Crashes harder when things go wrong • Slower to write - ruby is very concise and expressive Monday, 10 December 12 Options • Native java code • “Classic” C-extension • SWIG • RICE • RubyInline • FFI Monday, 10 December 12 Calling java from JRuby • Just do it - stupidly easy • JRuby handles most of the type conversion, method name conversion require 'java' java_import java.lang.System version = System.getProperties.get("java.runtime.version") version = System.properties["java.runtime.version"] Monday, 10 December 12 JRuby continued • Can subclass java classes in ruby • Can implement a java interface in ruby • Rescue/raise java exceptions from ruby • Blocks handled nicely java.lang.Thread.new do puts 'hi' end Monday, 10 December 12 C extensions • C code written using the MRI api - like the internals of ruby itself • Compatible with MRI, Rubinius, partly with jruby • Intricacies of the API not well documented (README.EXT, headers) • C api can change between versions (but then so can the ruby API) Monday, 10 December 12 What does a C extension do? Monday, 10 December 12 Write methods in C and attach them to ruby objects static VALUE rb_keychain_find(int argc, VALUE *argv, VALUE self){ ... } void Init_keychain(){ rb_cKeychain = rb_const_get(rb_cObject, rb_intern("Keychain")); rb_define_singleton_method(rb_cKeychain, "find", RUBY_METHOD_FUNC(rb_keychain_find), -1); } Monday, 10 December 12 Write methods in C and attach them to ruby objects static VALUE rb_keychain_find(int argc, VALUE *argv, VALUE self){ ... } void Init_keychain(){ rb_cKeychain = rb_const_get(rb_cObject, rb_intern("Keychain")); rb_define_singleton_method(rb_cKeychain, "find", RUBY_METHOD_FUNC(rb_keychain_find), -1); } Monday, 10 December 12 Write methods in C and attach them to ruby objects static VALUE rb_keychain_find(int argc, VALUE *argv, VALUE self){ ... } void Init_keychain(){ rb_cKeychain = rb_const_get(rb_cObject, rb_intern("Keychain")); rb_define_singleton_method(rb_cKeychain, "find", RUBY_METHOD_FUNC(rb_keychain_find), -1); } Monday, 10 December 12 Wrap native data structures SecKeychainRef keychainRef = ... VALUE result = Data_Wrap_Struct(rb_cKeychain, NULL, CFRelease, keychainRef); SecKeychainRef keychain=NULL; Data_Get_Struct(ruby_object, struct OpaqueSecKeychainRef, keychain); Monday, 10 December 12 Wrap native data structures SecKeychainRef keychainRef = ... VALUE result = Data_Wrap_Struct(rb_cKeychain, NULL, CFRelease, keychainRef); SecKeychainRef keychain=NULL; Data_Get_Struct(ruby_object, struct OpaqueSecKeychainRef, keychain); Monday, 10 December 12 Wrap native data structures SecKeychainRef keychainRef = ... VALUE result = Data_Wrap_Struct(rb_cKeychain, NULL, CFRelease, keychainRef); SecKeychainRef keychain=NULL; Data_Get_Struct(ruby_object, struct OpaqueSecKeychainRef, keychain); Monday, 10 December 12 Convert/check types • rb_float_new / RFLOAT_VALUE • CheckType(foo, T_STRING) • StringValueCStr(foo) • FIX2INT, INT2FIX, LL2NUM, ... • NIL_P • RTEST Monday, 10 December 12 Gets verbose quickly def find(first_or_all, kind, options={}) if options[:some_option] ... end end Monday, 10 December 12 static VALUE rb_keychain_find(int argc, VALUE *argv, VALUE self){ VALUE kind; VALUE options; VALUE first_or_all; rb_scan_args(argc, argv, "2:", &first_or_all, &kind, &options); Check_Type(first_or_all, T_SYMBOL); Check_Type(kind, T_STRING); if(options){ if(RTEST(rb_hash_aref(options, SYM2ID(rb_intern("some_option"))))) { ... } } } Monday, 10 December 12 static VALUE rb_keychain_find(int argc, VALUE *argv, VALUE self){ VALUE kind; VALUE options; VALUE first_or_all; rb_scan_args(argc, argv, "2:", &first_or_all, &kind, &options); Check_Type(first_or_all, T_SYMBOL); Check_Type(kind, T_STRING); if(options){ if(RTEST(rb_hash_aref(options, SYM2ID(rb_intern("some_option"))))) { ... } } } Monday, 10 December 12 static VALUE rb_keychain_find(int argc, VALUE *argv, VALUE self){ VALUE kind; VALUE options; VALUE first_or_all; rb_scan_args(argc, argv, "2:", &first_or_all, &kind, &options); Check_Type(first_or_all, T_SYMBOL); Check_Type(kind, T_STRING); if(options){ if(RTEST(rb_hash_aref(options, SYM2ID(rb_intern("some_option"))))) { ... } } } Monday, 10 December 12 static VALUE rb_keychain_find(int argc, VALUE *argv, VALUE self){ VALUE kind; VALUE options; VALUE first_or_all; rb_scan_args(argc, argv, "2:", &first_or_all, &kind, &options); Check_Type(first_or_all, T_SYMBOL); Check_Type(kind, T_STRING); if(options){ if(RTEST(rb_hash_aref(options, SYM2ID(rb_intern("some_option"))))) { ... } } } Monday, 10 December 12 Call a method with a block static VALUE doSomething(VALUE yielded_object, VALUE rb_context,int argc, VALUE *argv){ Context *context = NULL; Data_Get_Struct( rb_context, Context, context); ... return Qnil; } Context *context = ... VALUE wrapped_struct = Data_Wrap_Struct(rb_cFindContext, NULL,NULL, context); rb_block_call(rb_cUser, rb_intern("find_each"), 0, NULL, RUBY_METHOD_FUNC(doSomething), wrapped_struct); Monday, 10 December 12 Call a method with a block static VALUE doSomething(VALUE yielded_object, VALUE rb_context,int argc, VALUE *argv){ Context *context = NULL; Data_Get_Struct( rb_context, Context, context); ... return Qnil; } Context *context = ... VALUE wrapped_struct = Data_Wrap_Struct(rb_cFindContext, NULL,NULL, context); rb_block_call(rb_cUser, rb_intern("find_each"), 0, NULL, RUBY_METHOD_FUNC(doSomething), wrapped_struct); Monday, 10 December 12 Call a method with a block static VALUE doSomething(VALUE yielded_object, VALUE rb_context,int argc, VALUE *argv){ Context *context = NULL; Data_Get_Struct( rb_context, Context, context); ... return Qnil; } Context *context = ... VALUE wrapped_struct = Data_Wrap_Struct(rb_cFindContext, NULL,NULL, context); rb_block_call(rb_cUser, rb_intern("find_each"), 0, NULL, RUBY_METHOD_FUNC(doSomething), wrapped_struct); Monday, 10 December 12 • Anything is possible but it can be quite laborious • Not much typesafety - nearly everything is a VALUE • Sometimes unhelpful api naming: rb_str_new, rb_str_new2, rb_str_new3, rb_str_new4, rb_str_new5 Monday, 10 December 12 SWIG • Generates code for C extensions automatically from a marked up header file • can target many languages (ruby, python, php, ocaml, perl, ...) • generated code is pretty illegible • interfaces often feel unnatural to me Monday, 10 December 12 RICE • C++ library wrapping the ruby c api, smoothing inconsistencies • Makes wrapping C++ classes easy • type conversions handled via template functions • implements STL iterators for Array, Hash Monday, 10 December 12 • tries to leverage type safety • converts between C++ and ruby exceptions • Doesn’t seem very active - 6 commits in past year Monday, 10 December 12 RubyInline • Write C extensions without some of the hassle • generates, compiles and loads extension at runtime • doesn’t work in irb Monday, 10 December 12 require 'inline' class Factorial inline :C do |builder| builder.c_singleton <<-SRC long calculate(long n){ long result = 1; for(long i = 2; i<=n; i++){ result *= i; } return result; } SRC end end Monday, 10 December 12 require 'inline' class Factorial inline :C do |builder| builder.c_singleton <<-SRC long calculate(long n){ long result = 1; for(long i = 2; i<=n; i++){ result *= i; } return result; } SRC end end Monday, 10 December 12 static VALUE calculate(VALUE self, VALUE _n) { long n = NUM2LONG(_n); long result = 1; for(long i = 2; i<=n; i++){ result *= i; } return LONG2NUM(result); } #ifdef __cplusplus extern "C" { #endif void Init_Inline_Factorial_7b051bd7f8f35cf0422f74703bc98c19() { VALUE c = rb_cObject; c = rb_const_get(c, rb_intern("Factorial")); rb_define_singleton_method(c, "calculate", (VALUE(*)(ANYARGS))calculate, 1); } #ifdef __cplusplus } #endif Monday, 10 December 12 static VALUE calculate(VALUE self, VALUE _n) { long n = NUM2LONG(_n); long result = 1; for(long i = 2; i<=n; i++){ result *= i; } return LONG2NUM(result); } #ifdef __cplusplus extern "C" { #endif void Init_Inline_Factorial_7b051bd7f8f35cf0422f74703bc98c19() { VALUE c = rb_cObject; c = rb_const_get(c, rb_intern("Factorial")); rb_define_singleton_method(c, "calculate", (VALUE(*)(ANYARGS))calculate, 1); } #ifdef __cplusplus } #endif Monday, 10 December 12 static VALUE calculate(VALUE self, VALUE _n) { long n = NUM2LONG(_n); long result = 1; for(long i = 2; i<=n; i++){ result *= i; } return LONG2NUM(result); } #ifdef __cplusplus extern "C" { #endif void Init_Inline_Factorial_7b051bd7f8f35cf0422f74703bc98c19() { VALUE c = rb_cObject; c = rb_const_get(c, rb_intern("Factorial")); rb_define_singleton_method(c, "calculate", (VALUE(*)(ANYARGS))calculate, 1); } #ifdef __cplusplus } #endif Monday, 10 December 12 module Foo inline do |builder| builder.add_compile_flags '-x c++', '-lstdc++', '-Wall', '-Werror' builder.prefix <<-SRC # line #{__LINE__ + 1} "#{__FILE__}" class foo { ... } SRC builder.c_raw <<-SRC VALUE attr_1(VALUE self){ foo *data = NULL; Data_Get_Struct( self, foo, data); return INT2FIX(data->get_attr_1()); } SRC end end Monday, 10 December 12 module Foo inline do |builder| builder.add_compile_flags '-x c++', '-lstdc++', '-Wall', '-Werror' builder.prefix <<-SRC # line #{__LINE__ + 1} "#{__FILE__}" class foo { ... } SRC builder.c_raw <<-SRC VALUE attr_1(VALUE self){ foo *data = NULL; Data_Get_Struct( self, foo, data);
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages58 Page
-
File Size-