今更ながら拡張ライブラリを書いてみる
ふと気がついたら、Rubyのinstance_evalとmodule_evalの違いや、defとdefine_methodの違いとかを忘れていてショックだったので、気合を入れ直そうとRubyの拡張ライブラリを書いてみることに。
せっかくCなので、RBasic構造体のflagsにアクセスしてみようと思う。
参考 => 第2章 オブジェクト
こんな感じで書いてみた。
#include "ruby.h" static VALUE get_flags(VALUE self) { unsigned long r; switch (TYPE(self)) { case T_NIL: case T_SYMBOL: case T_TRUE: case T_FALSE: case T_FIXNUM: r = self; break; default: r = ((struct RBasic *)self)->flags; break; } return INT2NUM(r); } void Init_flagaccess() { VALUE module; module = rb_define_module("FlagAccess"); rb_define_method(module, "get_flags", get_flags, 0); }
はじめはVALUE self(大抵は実質ポインタ)が指しているRBasicのflagsを返すだけで良いかなぁ、と思っていたのだけど、nil, 小っちゃめな整数, シンボル、true, falseはポインタではなく値そのものが入っているので、そのまま返すようにした。
でこれをコンパイルする
komamitsu@potato:~/svn/misc/lab/ruby$ ruby -r mkmf -e 'create_makefile "flagaccess"' creating Makefile komamitsu@potato:~/svn/misc/lab/ruby$ make cc -I. -I/usr/lib/ruby/1.8/i486-linux -I/usr/lib/ruby/1.8/i486-linux -I. -D_FILE_OFFSET_BITS=64 -fPIC -fno-strict-aliasing -g -g -O2 -fPIC -c flagaccess.c cc -shared -o flagaccess.so flagaccess.o -L. -L/usr/lib -L. -Wl,-Bsymbolic-functions -rdynamic -Wl,-export-dynamic -lruby1.8 -lpthread -ldl -lcrypt -lm -lc
動作確認してみる前に、後ほど活躍するであろうruby内部の構造体型フラグ(in ruby.h)を…
#define T_NIL 0x01 #define T_OBJECT 0x02 #define T_CLASS 0x03 #define T_ICLASS 0x04 #define T_MODULE 0x05 #define T_FLOAT 0x06 #define T_STRING 0x07 #define T_REGEXP 0x08 #define T_ARRAY 0x09 #define T_FIXNUM 0x0a #define T_HASH 0x0b #define T_STRUCT 0x0c #define T_BIGNUM 0x0d #define T_FILE 0x0e
ではirbで確認.
ちなみに、nil, 小っちゃめな整数, シンボル、true, false以外は下6ビットが構造体型
komamitsu@potato:~/svn/misc/lab/ruby$ irb irb(main):001:0> require 'flagaccess' => true irb(main):002:0> class Object; include FlagAccess end => Object irb(main):003:0> 123.get_flags => 247 irb(main):004:0> 123.get_flags.to_s(2) => "11110111" # 1ビット右シフトすると0x7b(123) irb(main):005:0> "123".get_flags.to_s(2) => "10000000000111" # 構造体型: T_STRING irb(main):006:0> [].get_flags.to_s(2) => "1001" # 構造体型: T_ARRAY irb(main):007:0> {}.get_flags.to_s(2) => "1011" # 構造体型: T_HASH irb(main):009:0> self.class.get_flags.to_s(2) => "11" # 構造体型: T_CLASS irb(main):010:0> nil.get_flags.to_s(2) => "100" # nilは4 irb(main):011:0> true.get_flags.to_s(2) => "10" # trueは2 irb(main):012:0> false.get_flags.to_s(2) => "0" # falseは0 irb(main):013:0> :a.get_flags.to_s(2) => "10000011101100100001110" # シンボル irb(main):015:0> class << self; self end.get_flags.to_s(2) => "100000000011" # 特異クラス & 構造体型: T_CLASS irb(main):021:0> /123/.get_flags.to_s(2) => "10000000000001000" # 構造体型: T_REGEXP
なかなか楽しい。