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.

9d2ea021919ff81e02d48530aae191bd?s=128

Boris Bügling

July 31, 2015
Tweet

Transcript

  1. 6.

    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. 7.

    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. 8.

    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. 9.
  5. 10.
  6. 21.

    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
  7. 22.

    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
  8. 24.

    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
  9. 25.

    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