lispとrubyとpythonと その4 Cライブラリの呼び出し(ruby)

つぎはruby
rubyだとCライブラリを呼び出すというよりはrubyのクラスをCで書く感じ。
ruby1.9でためしたんだけど、1.9では
RSTRING(xxx).ptr
とか
RSTRING(xxx).len
ではなくて
RSTRING_LEN(xxx)
RSTRING_PTR(xxx)
と書くみたい。

参考はこのへん。
http://i.loveruby.net/w/RubyExtensionProgrammingGuide.html
http://wiki.livedoor.jp/aqualung/d/Ruby%a4%ce%b3%c8%c4%a5%a5%e9%a5%a4%a5%d6%a5%e9%a5%ea%a4%ce%ba%ee%a4%ea%ca%fd#content_1_5_1

でcで書いた拡張ライブラリがこんなの。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ruby.h>

typedef struct 
{
  VALUE prefix;
  VALUE suffix;
} decorator;

void decorator_mark(decorator *p)
{
  rb_gc_mark(p->prefix);
  rb_gc_mark(p->suffix);
}

void decorator_free(decorator *p)
{
}
 
VALUE decorator_alloc(VALUE cls)
{
  decorator *p = ALLOC(decorator);
  return Data_Wrap_Struct(cls,decorator_mark,decorator_free,p);
}

VALUE decorator_initialize(VALUE self,VALUE prefix,VALUE suffix)
{
  decorator *p;
  Data_Get_Struct(self, decorator, p);
  Check_Type(prefix,T_STRING);
  Check_Type(suffix,T_STRING);

   p->prefix = prefix;
   p->suffix = suffix;

  return Qnil;
}

VALUE decorator_get_string(VALUE self,VALUE str)
{
  Check_Type(str,T_STRING);

  decorator *p;
  Data_Get_Struct(self, decorator, p);

  char* buf = ALLOC_N(char,RSTRING_LEN(p->prefix) + RSTRING_LEN(str) + RSTRING_LEN(p->suffix) + 1);
  memset(buf,0,RSTRING_LEN(p->prefix) + RSTRING_LEN(str) + RSTRING_LEN(p->suffix) + 1);
  strncat(buf,RSTRING_PTR(p->prefix),RSTRING_LEN(p->prefix));
  strncat(buf,RSTRING_PTR(str),RSTRING_LEN(str));
  strncat(buf,RSTRING_PTR(p->suffix),RSTRING_LEN(p->suffix));

  return rb_str_new2(buf);
}

void Init_decorator()
{
  VALUE d;
  
  d = rb_define_class("Decorator", rb_cObject);
  rb_define_alloc_func(d, decorator_alloc);
  rb_define_private_method(d, "initialize", decorator_initialize, 2);
  rb_define_method(d, "get_string", decorator_get_string, 1);
}

で、こんな感じのスクリプトを実行

ruby -r mkmf -e 'create_makefile("decorator")'
make # ruby

実行するとmakeまで走ってdecorator.soができる。

rubyからの呼び出しはこんな感じ。

require 'decorator.so'

d = Decorator.new "start:" , ":end"
puts d.get_string("aaa")