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"] Saturday, 28 September 13
a java interface in ruby • Rescue/raise java exceptions from ruby • Blocks handled nicely java.lang.Thread.new do puts 'hi' end Saturday, 28 September 13
api - like the internals of ruby itself • Intricacies of the API not well documented (README.EXT, headers) • C api can change between versions (but then so can the ruby API) Saturday, 28 September 13
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 Saturday, 28 September 13
up header file • Can target many languages (ruby, python, php, ocaml, perl, ...) • Generated code is pretty illegible • Interfaces often unnatural - need wrapping SWIG Saturday, 28 September 13
steps required • great for small hotspots / simple interfaces • raises CompilationError on failure - easy to include pure ruby & native version • Advantages diminish as size grows, eg in general no syntax highlighting, code completion. Saturday, 28 September 13
layout :tz_minuteswest, :int, :tz_dsttime, :int end class Timeval < FFI::Struct layout :tv_sec, :time_t, :tv_usec, :suseconds_t end attach_function :gettimeofday, [Timeval, Timezone], :int end out = LibC::Timeval.new LibC.gettimeofday(out, nil) puts "the time according to libc is #{out[:tv_sec]}" Saturday, 28 September 13
layout :tz_minuteswest, :int, :tz_dsttime, :int end class Timeval < FFI::Struct layout :tv_sec, :time_t, :tv_usec, :suseconds_t end attach_function :gettimeofday, [Timeval, Timezone], :int end out = LibC::Timeval.new LibC.gettimeofday(out, nil) puts "the time according to libc is #{out[:tv_sec]}" Saturday, 28 September 13
layout :tz_minuteswest, :int, :tz_dsttime, :int end class Timeval < FFI::Struct layout :tv_sec, :time_t, :tv_usec, :suseconds_t end attach_function :gettimeofday, [Timeval, Timezone], :int end out = LibC::Timeval.new LibC.gettimeofday(out, nil) puts "the time according to libc is #{out[:tv_sec]}" Saturday, 28 September 13
layout :tz_minuteswest, :int, :tz_dsttime, :int end class Timeval < FFI::Struct layout :tv_sec, :time_t, :tv_usec, :suseconds_t end attach_function :gettimeofday, [Timeval, Timezone], :int end out = LibC::Timeval.new LibC.gettimeofday(out, nil) puts "the time according to libc is #{out[:tv_sec]}" Saturday, 28 September 13
C api, FFI, RICE • https://github.com/fcheung/keychain ~ 1000 lines of ruby, ~ 600 lines of real code • https://github.com/fcheung/keychain_c/ ~ 700 lines of C + a small amount of ruby to autogenerate accessors • https://github.com/fcheung/keychain_rice ~ 800 lines of C++ (no auto generated accessors) • All versions pass the same set of specs Saturday, 28 September 13
Sec.SecKeychainUnlock self, password.bytesize, password, 1 else status = Sec.SecKeychainUnlock self, 0, nil, 0 end Sec.check_osstatus status end FFI Saturday, 28 September 13
• Small performance penalty over a C extension (often irrelevant) • Much easier/nicer (in my experience) than writing C extensions • Handles many difficult edge cases FFI summary Saturday, 28 September 13
required • Better compatibility across implementations • All glue code is ruby - easier to write and easier to be ruby-like • less typechecking/wrapping/unwrapping boilerplate • FFI yielded more reusable components (core_foundation gem) Saturday, 28 September 13
/ enums / #defines are a pain • you still need to understand C, memory management • Some incompatibilities between FFI implementations • harder to create narrow slices of functionality • Still crashes just as hard if you mess it up Saturday, 28 September 13
• Objective-C supports run time introspection of classes, methods etc. • Can we use some ruby metaprogramming to bridge the two? Saturday, 28 September 13