Extending Ruby with C

I've generally found that the APIs for bridging the gap between Perl and C are either cryptic (XS) or fragile (Inline::C). While Python is better in some ways, I still find its C API rather difficult to read. Tools such as SWIG can help alleviate this problem, but you still need to write a bunch of glue code to bridge the gap between the high-level agile languages and the low-level C code.

When I first looked at doing the same kind of thing for Ruby, a whole new world opened up. The APIs are simple, to the point where I was up and running in minutes rather than hours.

A nice tutorial article on extending Ruby by providing access to a C library (GenX).

It has been awhile since we discussed language extension mechanisms and multi-language programming, yet these techniques are quite important when building real life systems.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

The API of Extension Languages

The API of an extension language does not have to be dreadful. For a comparison and nice solution for Lua see this paper:

Norman Ramsey: Embedding an Interpreted Language Using Higher-Order Functions and Types. In ACM SIGPLAN 2003 Workshop on Interpreters, Virtual Machines and Emulators, pages 6-14, June 2003. Available from http://www.eecs.harvard.edu/~nr/pubs/embed-abstract.html

C libraries can usually be accessed via dynamic linker

C libraries can usually be accessed via dynamic linker. Ruby dl library makes this very simple:

require 'dl'
require 'dl/import'

module Libc
  extend DL::Importable
  dlload "/lib/libc.so.6"
  extern "char* ttyname(int)"
end

puts Libc.ttyname(0)

Example run:

$ ruby dltest.rb
/dev/pts/102

Accessing libraries through the file system

Along with cryptic and fragile, don't forget obscure. Here's an idea from the people making userspace device drivers in Linux:
"One particularly interesting application of FUSD that we've found very useful is as a way to let regular user-space libraries export device file APIs. For example, imagine you had a library which factored large composite numbers. Typically, it might have a C interface--say, a function called int *factorize(int bignum). With FUSD, it's possible to create a device file interface--say, a device called /dev/factorize to which clients can write(2) a big number, then read(2) back its factors.

This may sound strange, but device file APIs have at least three advantages over a typical library API. First, it becomes much more language independent--any language that can make system calls can access the factorization library. Second, the factorization code is running in a different address space; if it crashes, it won't crash or corrupt the caller. Third, and most interestingly, it is possible to use select(2) to wait for the factorization to complete. select(2) would make it easy for a client to factor a large number while remaining responsive to other events that might happen in the meantime. In other words, FUSD allows normal user-space libraries to integrate seamlessly with UNIX's existing, POSIX-standard event notification interface: select(2)."

He missed something important

Namely the Boost Python Library which makes the transition between C++ and Python seamless. You can write C++ that looks and acts just like Python code, if you want. SWIG and the Python C API are not required. Next, it's deep, but I have been meaning to post a link to Boost Python internals for some time.