今更ながら拡張ライブラリを書いてみる

ふと気がついたら、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

なかなか楽しい。