海野秀之(うんのひでゆき)の外部記憶
Twitter (twilog) / RSS / アンテナ / ぶくま
CD 買いにいかなあかん → こおた。聞くのがたのしみ。
『オブジェクト指向スクリプト言語 Ruby』をみながら、 拡張ライブラリを書いてみた。
Ruby は拡張ライブラリを書きやすい(そのために実行速度が犠牲になっている面すらある) と聞いた(読んだ)ことはあったのだが、実際に書いたことはなかった。 書いてみたら、いや、本当に書きやすい。
/*
extlibsample.c
To create ExtLibSample.so:
ruby -r mkmf -e 'create_makefile("ExtLibSample")' and make
*/
#include "ruby.h"
#include <math.h>
/* First example:
def euclid_norm_2d(x, y) # x, y: Float.
sqrt(x*x + y*y)
end
*/
static VALUE
euclid_norm_2d(self, vx, vy)
VALUE self, vx, vy;
{
double x, y;
Check_Type(vx, T_FLOAT);
Check_Type(vy, T_FLOAT);
x = RFLOAT(vx)->value;
y = RFLOAT(vy)->value;
return rb_float_new(sqrt(x * x + y * y));
}
/* Second example:
def euclid_norm(a) # a: Array.
sum = 0
a.each {|v| v = v.float; sum += v*v}
sqrt(sum)
end
*/
static VALUE
euclid_norm(self, a)
VALUE self, a;
{
int i, size;
double v, sum = 0.0;
VALUE t;
Check_Type(a, T_ARRAY);
size = FIX2INT(rb_funcall(a, rb_intern("size"), 0));
for(i = 0; i < size; i++){
t = rb_funcall(a, rb_intern("[]"), 1, INT2FIX(i));
t = rb_funcall(t, rb_intern("to_f"), 0);
v = RFLOAT(t)->value;
sum += v * v;
}
return rb_float_new(sqrt(sum));
}
Init_ExtLibSample()
{
VALUE mExtLibSample = rb_define_module("ExtLibSample");
rb_define_module_function(mExtLibSample, "euclid_norm_2d", euclid_norm_2d, 2);
rb_define_module_function(mExtLibSample, "euclid_norm", euclid_norm, 1);
}
呪文をいっぱい唱える必要がなくて、ありがたい。
書いてみたのは、まったく実用的でない、練習のための例ですが、 以下のことがらを含んでいます。
| Ruby オブジェクトの型チェック | Check_Type(obj, T_FLOAT) |
| Ruby Float → C double | RFLOAT(obj)->value |
| C double → Ruby Float | rb_float_new((double) x) |
| Ruby Fixnum → C int | FIX2INT(obj) |
| C int → Ruby Fixnum | INT2FIX(1) |
| Ruby オブジェクトのメソッド呼び出し | rb_funcall(obj, rb_intern("[]"), 1, INT2FIX(i)) |
| モジュール関数の追加 | (略) |
動かしてみるには、次のようにします:
% ruby -r mkmf -e 'create_makefile("ExtLibSample")'
% gmake
% irb
irb(main):001:0> require 'ExtLibSample'
=> true
irb(main):002:0> include ExtLibSample
=> Object
irb(main):003:0> euclid_norm_2d(2.0, 3.0)
=> 3.60555127546399
irb(main):004:0> euclid_norm([1.0, 2.0, 3.0])
=> 3.74165738677394
『オブジェクト指向スクリプト言語 Ruby』の8章に書かれている内容を まったく知らないとつらいかな。
ところで、 ワード境界にアラインされた C オブジェクトのポインタ値が偶数になるというのは 言語仕様の一部ではないような気がするのだが(未確認)、 それが奇数になるようなアーキテクチャを思いつかない。 ワード長が9バイトとか?