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

Calling Native Code from Ruby

Calling Native Code from Ruby

This talk gives a quick introduction into the not so well known Fiddle, which has been a part of MRI since 1.9. It allows direct interaction with C code via a Foreign Function Interface, which enables wrapping native code without compiling C extensions, making the lives of users of your projects much easier.

Avatar for Boris Bügling

Boris Bügling

July 31, 2015
Tweet

More Decks by Boris Bügling

Other Decks in Programming

Transcript

  1. Xcodeproj issues $ bundle exec rake ... linking shared-object xcodeproj_ext.bundle

    clang: error: unknown argument: '-multiply_definedsuppress' [-Wunused-command-line-argument-hard-error-in-future] clang: note: this will be a hard error (cannot be downgraded to a warning) in the future make: *** [xcodeproj_ext.bundle] Error 1
  2. Xcodeproj issues $ gem install xcodeproj Building native extensions. This

    could take a while... ERROR: Error installing xcodeproj: ERROR: Failed to build gem native extension. /Users/siuying/.rvm/rubies/ruby-1.9.3-p194/bin/ruby extconf.rb checking for -std=c99 option to compiler... *** extconf.rb failed *** Could not create Makefile due to some reason, probably lack of necessary libraries and/or headers. Check the mkmf.log file for more details. You may need configuration options.
  3. Xcodeproj issues creating Makefile make [...] make install /usr/bin/install -c

    -m 0755 xcodeproj_ext.bundle ./.gem.20130406-29555-zd0wy8 ERROR: While executing gem ... (NoMethodError) undefined method `join' for nil:NilClass
  4. def self.extern_image(image, symbol, parameter_types, return_type) symbol = symbol.to_s create_function =

    symbol.include?('Create') function_cache_key = "@__#{symbol}__" define_singleton_method(symbol) do |*args| unless function = instance_variable_get(function_cache_key) function = Fiddle::Function.new(image[symbol], parameter_types, return_type) instance_variable_set(function_cache_key, function) end result = function.call(*args) create_function ? CFAutoRelease(result) : result end end
  5. PATH = '/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation' def self.image @image ||= Fiddle.dlopen(PATH) end def

    self.extern(symbol, parameter_types, return_type) extern_image(image, symbol, parameter_types, return_type) end extern :CFDataGetLength, [CFTypeRef], CFIndex extern :CFDataGetBytePtr, [CFTypeRef], VoidPointer def self.CFStringToRubyString(string) data = CFStringCreateExternalRepresentation(NULL, string, KCFStringEncodingUTF8, 0) if data.null? raise TypeError, 'Unable to convert CFStringRef.' end bytes_ptr = CFDataGetBytePtr(data) result = bytes_ptr.to_str(CFDataGetLength(data)) result.force_encoding(Encoding::UTF_8) result end
  6. def self.objc_msgSend(args, return_type = CoreFoundation::VoidPointer) arguments = [CoreFoundation::VoidPointer, CoreFoundation::VoidPointer] +

    args Fiddle::Function.new(image['objc_msgSend'], arguments, return_type) end class CFDictionary < NSObject public def initialize(dictionary) @dictionary = dictionary end def plistDescriptionUTF8Data selector = 'plistDescriptionUTF8Data' return nil unless NSObject.respondsToSelector(@dictionary, selector) plistDescriptionUTF8Data = CFDictionary.objc_msgSend([]) plistDescriptionUTF8Data.call( @dictionary, CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString(selector))) end end
  7. Recap • C extensions have a bad UX • Fiddle

    provides a way to use native code dynamically • Which eliminates all the compilation hassle • We can even call Objective-C if we want