知らないと損するRubiniusのFFI

えっとタイトルは

ホットエントリーメーカーと言う事で、本当に便利な時代だよね。
まったく損しないと思います。えぇ。
 

FFIってなんぞ

FFIとは - はてなキーワード
http://practical-scheme.net/wiliki/wiliki.cgi?Gauche%3AFFI
が詳しいです。
意味は名前の通りで「Foreign Function Interface」(外部関数の橋渡し)って感じ。
まぁ、Cで書いたコードにアクセスできる道を提供するって感じ。
 

動機

RubyFFI作って、GCとかexactにしたら?って事を言われた。
exactにする事は私自身乗り気ではないけど、
FFIはあってもいいんじゃないか思うのでいつか作ってみたいなぁーとか思った。
(その場合は拡張ライブラリという事になるので、若干気が楽そう)
基本的にHugeDomains.com - LifeGoo.com is for sale (Life Goo)の翻訳っぽくなりますので、英語読める人はそっちを読むと素敵。
関係ないけど、Rubiniusと聞くとEvanの渋い声を思い出す(RubyKaigi2008)。「__METHOD__」だ。
天丼したりスカしたりしてとってもお茶目だったなぁ。
 

CRubyとの比較

そういえば、皆さんはCRubyの拡張ライブラリを書いたことがあるだろうか。
私は無い。(ちょっ)

CRubyで

Cのprintfを呼んでみたいと思うとこうなる。

#include <ruby.h>
 
static VALUE
dummy_printf(VALUE self, VALUE format, VALUE num)
{
    int rlt = printf(RSTRING_PTR(format), NUM2INT(num));
    return INT2FIX(rlt);
}
 
void Init_printf()
{
    /* register the method to the Kernel module */
    rb_define_method(rb_mKernel, "dummy_printf",
                     RUBY_METHOD_FUNC(dummy_printf), 2);
}

 
そして、mkmfでmakeファイルを作る事になる。

require 'mkmf'
 
create_makefile('printf')

mkmfって何だ。すごいなぁ。
 

irb(main):001:0> require 'printf'
=> true
irb(main):002:0> dummy_printf "hi! This is the number: %d\n", 6
hi! This is the number: 6
=> 26

やっと呼び出せた。実質printfを呼びたかっただけなのだか、これくらい長くなってしまった。
(というか他にもやり方あるような気がするけど、こんな拡張ライブラリ書かなくても。。。)
 

さて、RubiniusのFFIでは?
module Kernel
  attach_function 'printf', :dummy_printf, [:string, :int], :int
 
  def call_printf(*args)
    Kernel.dummy_printf(*args)
  end
end

そして、

irb(main):001:0> call_printf "hi! This is the number: %d\n", 6
hi! This is the number: 6
=> 26

おー!これはシンプルでさっぱりしてるね!
 

解説

メソッドを少し解説
 

attach_function
attach_function 'printf', :dummy_printf, [:string, :int], :int

このメソッドの仕様はこんな感じらしい
 
Module#attach_function(name, a3, a4, a5=nil)

  • call sequence:
    • attach_function(func_name, method_name, arg_types, ret_type) => method
    • attach_function(func_name, arg_types, ret_type) => method
  • params:
    • func_name: Cの呼び出したい関数. つまり、"printf"だ.
    • method_name: Ruby側でラップしたい関数. この場合. "dummy_printf"になる.
    • arg_types: 引数の型ね. つまり. [:string, :int].
    • ret_type: 戻り値の型ね. つまり. :int.

 
.soファイルとか指定したかったら@ffi_libの中にパスを設定する

module Foo
  set_ffi_lib("libz", "libreadline") # , ...) 設定したいだけどうぞ
end

 

続きは

(執筆中)
と言って逃げる