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

Native Extensions Served 3 Ways

Native Extensions Served 3 Ways

This talk was given at Garden City Ruby Conf 2014

Avatar for Tejas Dinkar

Tejas Dinkar

January 03, 2014
Tweet

More Decks by Tejas Dinkar

Other Decks in Technology

Transcript

  1. about.talk • Expect to see lots of code • Will

    have about 5 minutes for questions • Please laugh at my jokes! • Will cover C Extensions, FFI and SWIG
  2. Native Extensions • Integrate with new libraries • Improve Performance

    of critical code • Write code that works across languages • Feel super 1337
  3. Let’s talk about Python • Pythonista’s in the house? •

    Yes, I’m trolling you! http://montgomeryq.blogspot.in/2011/05/random-illustration-tuesday-python-ruby.html
  4. #include "Python.h" #include "ruby.h" ! static PyObject *python_ruby_eval(PyObject *self, PyObject

    *string) { VALUE val = rb_eval_string(PyString_AsString(string)); switch(TYPE(val)) { case T_FIXNUM: return PyInt_FromLong(FIX2INT(val)); case T_STRING: return PyString_FromString(StringValuePtr(val)); default: return Py_None; // Can handle these cases later } } ! static PyMethodDef module_functions[] = { { "eval", python_ruby_eval, METH_O, "Evaluate Ruby Code" }, { NULL } }; ! void initruby(void) { ruby_init(); Py_InitModule3("ruby", module_functions, "A ruby module for python."); }
  5. #include "Python.h" #include "ruby.h" ! static PyObject *python_ruby_eval(PyObject *self, PyObject

    *string) { VALUE val = rb_eval_string(PyString_AsString(string)); switch(TYPE(val)) { case T_FIXNUM: return PyInt_FromLong(FIX2INT(val)); case T_STRING: return PyString_FromString(StringValuePtr(val)); default: return Py_None; // Can handle these cases later } } ! static PyMethodDef module_functions[] = { { "eval", python_ruby_eval, METH_O, "Evaluate Ruby Code" }, { NULL } }; ! void initruby(void) { ruby_init(); Py_InitModule3("ruby", module_functions, "A ruby module for python."); }
  6. #include "Python.h" #include "ruby.h" ! static PyObject *python_ruby_eval(PyObject *self, PyObject

    *string) { VALUE val = rb_eval_string(PyString_AsString(string)); switch(TYPE(val)) { case T_FIXNUM: return PyInt_FromLong(FIX2INT(val)); case T_STRING: return PyString_FromString(StringValuePtr(val)); default: return Py_None; // Can handle these cases later } } ! static PyMethodDef module_functions[] = { { "eval", python_ruby_eval, METH_O, "Evaluate Ruby Code" }, { NULL } }; ! void initruby(void) { ruby_init(); Py_InitModule3("ruby", module_functions, "A ruby module for python."); }
  7. #include "Python.h" #include "ruby.h" ! static PyObject *python_ruby_eval(PyObject *self, PyObject

    *string) { VALUE val = rb_eval_string(PyString_AsString(string)); switch(TYPE(val)) { case T_FIXNUM: return PyInt_FromLong(FIX2INT(val)); case T_STRING: return PyString_FromString(StringValuePtr(val)); default: return Py_None; // Can handle these cases later } } ! static PyMethodDef module_functions[] = { { "eval", python_ruby_eval, METH_O, "Evaluate Ruby Code" }, { NULL } }; ! void initruby(void) { ruby_init(); Py_InitModule3("ruby", module_functions, "A ruby module for python."); }
  8. #include "Python.h" #include "ruby.h" ! static PyObject *python_ruby_eval(PyObject *self, PyObject

    *string) { VALUE val = rb_eval_string(PyString_AsString(string)); switch(TYPE(val)) { case T_FIXNUM: return PyInt_FromLong(FIX2INT(val)); case T_STRING: return PyString_FromString(StringValuePtr(val)); default: return Py_None; // Can handle these cases later } } ! static PyMethodDef module_functions[] = { { "eval", python_ruby_eval, METH_O, "Evaluate Ruby Code" }, { NULL } }; ! void initruby(void) { ruby_init(); Py_InitModule3("ruby", module_functions, "A ruby module for python."); }
  9. #include "Python.h" #include "ruby.h" ! static PyObject *python_ruby_eval(PyObject *self, PyObject

    *string) { VALUE val = rb_eval_string(PyString_AsString(string)); switch(TYPE(val)) { case T_FIXNUM: return PyInt_FromLong(FIX2INT(val)); case T_STRING: return PyString_FromString(StringValuePtr(val)); default: return Py_None; // Can handle these cases later } } ! static PyMethodDef module_functions[] = { { "eval", python_ruby_eval, METH_O, "Evaluate Ruby Code" }, { NULL } }; ! void initruby(void) { ruby_init(); Py_InitModule3("ruby", module_functions, "A ruby module for python."); }
  10. #include "Python.h" #include "ruby.h" ! static PyObject *python_ruby_eval(PyObject *self, PyObject

    *string) { VALUE val = rb_eval_string(PyString_AsString(string)); switch(TYPE(val)) { case T_FIXNUM: return PyInt_FromLong(FIX2INT(val)); case T_STRING: return PyString_FromString(StringValuePtr(val)); default: return Py_None; // Can handle these cases later } } ! static PyMethodDef module_functions[] = { { "eval", python_ruby_eval, METH_O, "Evaluate Ruby Code" }, { NULL } }; ! void initruby(void) { ruby_init(); Py_InitModule3("ruby", module_functions, "A ruby module for python."); }
  11. #include "Python.h" #include "ruby.h" ! static PyObject *python_ruby_eval(PyObject *self, PyObject

    *string) { VALUE val = rb_eval_string(PyString_AsString(string)); switch(TYPE(val)) { case T_FIXNUM: return PyInt_FromLong(FIX2INT(val)); case T_STRING: return PyString_FromString(StringValuePtr(val)); default: return Py_None; // Can handle these cases later } } ! static PyMethodDef module_functions[] = { { "eval", python_ruby_eval, METH_O, "Evaluate Ruby Code" }, { NULL } }; ! void initruby(void) { ruby_init(); Py_InitModule3("ruby", module_functions, "A ruby module for python."); }
  12. void Init_String(void) {! rb_cString = rb_define_class("String", rb_cObject);! // ...! rb_define_method(rb_cString,

    "eql?", rb_str_eql, 1);! rb_define_method(rb_cString, "==", rb_str_equal, 1);! // ...! rb_define_method(rb_cString, "insert", rb_str_insert, 2);! rb_define_method(rb_cString, "length", rb_str_length, 0);! // ...! }! ! static VALUE rb_str_eql(VALUE self, VALUE str2)! {! if (self == str2) return Qtrue;! if (!RB_TYPE_P(str2, T_STRING)) return Qfalse;! return str_eql(self, str2);! }! string.c
  13. void Init_String(void) {! rb_cString = rb_define_class("String", rb_cObject);! // ...! rb_define_method(rb_cString,

    "eql?", rb_str_eql, 1);! rb_define_method(rb_cString, "==", rb_str_equal, 1);! // ...! rb_define_method(rb_cString, "insert", rb_str_insert, 2);! rb_define_method(rb_cString, "length", rb_str_length, 0);! // ...! }! ! static VALUE rb_str_eql(VALUE str2, VALUE str2)! {! if (self == str2) return Qtrue;! if (!RB_TYPE_P(str2, T_STRING)) return Qfalse;! return str_eql(self, str2);! }! string.c
  14. void Init_String(void) {! rb_cString = rb_define_class("String", rb_cObject);! // ...! rb_define_method(rb_cString,

    "eql?", rb_str_eql, 1);! rb_define_method(rb_cString, "==", rb_str_equal, 1);! // ...! rb_define_method(rb_cString, "insert", rb_str_insert, 2);! rb_define_method(rb_cString, "length", rb_str_length, 0);! // ...! }! ! static VALUE rb_str_eql(VALUE str2, VALUE str2)! {! if (self == str2) return Qtrue;! if (!RB_TYPE_P(str2, T_STRING)) return Qfalse;! return str_eql(self, str2);! }! string.c
  15. Foreign Function Interface • A Ruby DSL • Works across

    all Ruby Implementations • Converts to and from C primitives for you
  16. example require 'ffi'! ! module MyLib! extend FFI::Library! ffi_lib 'c'!

    attach_function :puts, [:string], :int! end! ! MyLib.puts 'Hello, World using libc!'
  17. another example require 'ffi'! ! module MyMathLib! extend FFI::Library! ffi_lib

    'm'! attach_function :pow, [:double, :double],! :double! end! ! MyMathLib.pow(4, 5) # => 1024.0
  18. Lots of built in types Numbers!! ! ! ! !

    Character!! ! ! ! ! Other! :int! ! ! ! ! ! ! :char!! ! ! ! ! ! ! :pointer! :short! ! ! ! ! ! :string! :long! :double! :float!
  19. Foreign Function Interface • Probably your best solution • It’s

    really easy • Do your modelling in Ruby • Still have to worry about GC • Sadly, no C++ without wrapping
  20. SWIG • Simplified Wrapper and Interface Generator • Annotate your

    C/C++ header files • It generates native extensions for languages • About 20 languages currently supported
  21. The Magic • Takes an interface file • Auto generates

    code to make it work • For ruby, it’s a `regular’ C extension • For python, it’s a a .c and .py file • For Java it’s a JNI interface • Still need to do your own GC
  22. The Rectangle class Rectangle! {! int length;! int breadth;! !

    public:! Rectangle(int length, int breadth);! int area();! };
  23. The Rectangle class Rectangle! {! int length;! int breadth;! !

    public:! Rectangle(int length, int breadth);! int area();! }; #ifdef SWIG %module shape %{ %} SWIG Stuff Here
  24. TL;DR • Native Extensions are fun and easy to build

    • The three big tools • You want to pick FFI if you don’t maintain the lib • SWIG may be better if you are a maintainer
  25. Thank You Many Questions? wow so native so extension wow

    no python such easy such performance super integration ruby = win