Ruby拡張ライブラリを書いてみる。

rubyソースのdir.cを見ながら書いてみた。

  • インスタンスにCデータを保存する(今回はint)。
  • そのデータを元に obj.step(2) {|v| ... } させる。

簡単過ぎ。ドキュメントが無いのが悔やまれるが言わないのが約束というものなんでしょう。

  • sample.cpp
#include "ruby.h"

class Sample
{
public:
  Sample() {
    m_value = 0;
  };
  virtual ~Sample() {
  };
  static void free(void* obj) {
    delete (Sample*)obj;
  };

  int add(int v) {
    m_value += v;
    return m_value;
  };
private:
  int m_value;
};

static VALUE sample_alloc(VALUE self)
{
  return Data_Wrap_Struct(self, 0, Sample::free, new Sample);
}

static VALUE sample_add(VALUE self, VALUE val)
{
  Sample* s;
  Data_Get_Struct(self, Sample, s);   // 失敗すると TypeError: wrong argument type false (expected Data)
  return INT2NUM(s->add(NUM2INT(val)));
}

static VALUE sample_step(VALUE self, VALUE val)
{
  Sample* s;
  Data_Get_Struct(self, Sample, s);
  int step = NUM2INT(val);

  for(int it=(s->add(0)); it>=0; it-=step) {
    rb_yield(INT2NUM(it));
  }
  return Qtrue;
}


extern "C" void Init_sample(void)
{
  VALUE rb_cSample;

  rb_cSample = rb_define_class("Sample", rb_cObject);
  rb_define_alloc_func(rb_cSample, sample_alloc);
  rb_define_method(rb_cSample, "add", (VALUE(*)(ANYARGS))sample_add, 1);
  rb_define_method(rb_cSample, "step", (VALUE(*)(ANYARGS))sample_step, 1);
}
  • extconf.rb
require 'mkmf'
$CFLAGS="-g -O2"
$LDFLAGS="-lstdc++"
create_makefile('sample')
$ ruby extconf.rb 
creating Makefile
$ make
gcc -shared -o sample.so sample.o -L. -L/usr/local/lib -Wl,-R/usr/local/lib -lstdc++
  -Wl,-R -Wl,/usr/local/lib -L/usr/local/lib -lruby  -lrt -ldl -lcrypt -lm   -lc
  • 実行
$ irb -r sample
irb(main):001:0> s = Sample.new
=> #<Sample:0xb7eba430>
irb(main):002:0> s.add 10
=> 10
irb(main):003:0> s.step(3) { |v| puts v }
10
7
4
1
=> true
irb(main):004:0>