Writing Inline C in Your Ruby Code

Credit: Garrett Rooney

Problem

You want to implement small portions of your program in C without going to the trouble of creating a C extension to Ruby.

Solution

Embed C code right in your Ruby program, and let RubyInline (available as the rubyinline gem) create an extension automatically.

For example, if you want to use Cs stdio functions to copy a file, you can write RubyInline code like this:[1]

[1] RubyInline won work from within irb, so this is a standalone program.

#!/usr/bin/ruby -w # copy.rb require ubygems require inline class Copier inline do |builder| builder.c < 0) { fwrite(buffer, 1, nread, dest_f); nread = fread(buffer, 1, 1024, source_f); } } END end end

The C function copy_file now exists as an instance method of Copier:

open(source.txt, w) { |f| f << Some text. } Copier.new.copy_file(source.txt, dest.txt) puts open(dest.txt) { |f| f.read }

Run this Ruby script, and youll see it copy the string "Some text." from source.txt to dest.txt.

Discussion

RubyInline is a framework that lets you embed other languages inside your Ruby code. It defines the Module# inline method, which returns a builder object. You pass the builder a string containing code written in a language other than Ruby, and the builder transforms it into something that you can call from Ruby.

When given C or C++ code (the two languages supported in the default RubyInline install), the builder objects writes a small extension to disk, compiles it, and loads it. You don have to deal with the compilation yourself, but you can see the generated code and compiled extensions in the .ruby_inline subdirectory of your home directory.

There are some limitations you should be aware of, though.

First, RubyInline only understands a limited subset of C and C++. The functions you embed can only accept and return arguments of the types char, unsigned, unsigned int, char *, int, long, and unsigned long.

If you need to use other types, RubyInline won be able to automatically generate the wrapper functions. Youll have to work around the problem using the inline.c_raw function to embed code that conforms to the Ruby C API, just like any other extension.

Second, if you e going to just run a script that uses RubyInline, youll need to have the Ruby development libraries and headers installed, along with a C/C++ compiler to actually build the extension.

Theres a way around this, though: RubyInline lets you generate a RubyGem package with a precompiled extension. See the RubyInline docs on the inline_package script for details.

As always, be careful to make sure that its actually worth the trouble to write C code. You should only rewrite part of a Ruby program in C if youve actually determined that Ruby spends a lot of time there. You should benchmark before and after your change, to make sure that you e making things better rather than worse. Writing C code within your Ruby code is much easier than writing a separate extension, but writing Ruby code is easier still.

See Also

Категории