This post originated from an RSS feed registered with Ruby Buzz
by Eric Hodel.
Original Post: On Ruby C Extensions
Feed Title: Segment7
Feed URL: http://blog.segment7.net/articles.rss
Feed Description: Posts about and around Ruby, MetaRuby, ruby2c, ZenTest and work at The Robot Co-op.
I’m not a great C coder, but I have implemented, cleaned up and read a few C extensions for Ruby and I’ve got some tips for writing C extensions
C is Bad
The C itself is your biggest enemy when writing C extensions. The more you write the harder it is for you to maintain and the harder it will be for anyone else to improve or fix your code in the future. The closer your C extension code is to the library’s API the better you can use Ruby to glue all the pieces together into a friendly interface.
Ruby is Good
With less C code and more Ruby code you end up a library that’s easier to refactor, adapt and extend. Generic data manipulation, simple math, convenience functions, etc. are all easier to write, test and debug when written in Ruby. There’s no need to recompile between changes, worry about compiler warnings, or fix type errors.
If you’re returning results from an operation, do as much of the work in Ruby as possible. If there are objects that can be created in Ruby create them in Ruby. If you get a struct sockaddr, unpack it on the Ruby side rather than calling the Socket methods from C using rb_funcall. If you’ve got an abstract representation of a flag bitfield, create the object on the Ruby side rather than calling rb_class_new_instance.
Check for what you need
mkmf.rb has loads of functions (dir_config, have_library, have_macro, have_func, have_type, have_struct_member, etc.) for determining whether or not you have everything you need to build your extension. When using these functions be sure to check the result and fail when what you need is missing.
Keep Up-to-date on the Ruby/C API
There’s tons of functions and macros for doing most of you need already built-in to Ruby. README.EXT has the overview, but there’s also ruby.h and intern.h which give you a list of functions you can use.
Use the friendly macros RSTRING_LEN, RSTRING_PTR and friends when playing with String, Array, etc. Use rb_define_alloc_func() with Data_Wrap_Struct(). Use RTEST and NIL_P for checking ruby results. Convert numbers with NUM2ULONG or NUM2INT, etc.
Play Nice with Threads
If you’re performing a blocking IO operation use rb_thread_wait_fd, rb_thread_fd_writable, etc. to keep other threads running. rb_thread_wait_for can be used for polling.
Don’t Repeat Yourself
While it is safe to repeatedly call rb_intern() if you have to invoke rb_funcall or rb_iv_set, it’s easier and prettier to use a static variable set from your Init function. Same for looking up classes using rb_path2class() or rb_define_class_under().
C is bad
It’s worth repeating. Writing C code is hard. Refactoring C code is hard. Debugging C code is hard. Do as little as possible in C and you’ll thank yourself down the road.