拡張ライブラリチュートリアルをやってみる#3

普通に C で書いた処理をうまく繋げられるのが,いい感じですね.

##配列を操作する 配列内の数値をすべて掛け合わせるような処理を実装する.

###処理実体 配列とその長さを受け取って,配列内の値を順に掛け算していく.

  • mul_all.c
float mul_all(array, nx)
     float array[];
     int nx;
{
  float result = 1.0;
  int i;

  for(i=0; i<nx; i++){
    result = result * array[i];
  }
  return(result);
}

これを検証するためのコード.

  • main.c
#include <stdio.h>

float mul_all(float array[], int nx);

int main()
{
  int n = 5;
  float a[n];
  int i;

  for(i=0; i<n; i++){
    a[i] = i + 1;
    printf("%f\n", a[i]);
  }
  printf("mul_all()=%f\n", mul_all(a, n));
}
% cc -o mul_all mul_all.c main.c
% ./mul_all
1.000000
2.000000
3.000000
4.000000
5.000000
mul_all()=120.000000

###C と Ruby の接続 そしてラッパーです.今回は NArray を使うそうなので,そのインストール.

% sudo gem install narray

ラッパーの記述

  • test3.c
#include "ruby.h"
#include "narray.h"

float mul_all(float array[], int nx);

VALUE wrap_mul_all(self, na)
     VALUE self, na;
{
  VALUE na2;
  struct NARRAY *n_na;
  float result;

  na2 = na_cast_object(na, NA_SFLOAT);
  GetNArray(na2,n_na);
  result = mul_all((float*)n_na->ptr, n_na->total);
  return( rb_float_new(result) );
}

void Init_test()
{
  VALUE module;

  rb_require("narray");
  module = rb_define_module("Test");
  rb_define_module_function(module, "mul_all", wrap_mul_all, 1);
}

wrap_mul_all の流れとしては,

  1. Ruby からやってきた VALUE 型構造体(na)を,NArray オブジェクト(na2)へ cast する (na_cast_object)
  2. それ(na)が NArray オブジェクトであれば,データ実体(NARRAY)へのポインタ(n_na)を返す(GetNArray)
  3. mul_all は float 型の配列を受け取り((float*)n_na->ptr),計算する
  4. 計算結果を Ruby の FLOAT 型に変換して返す (rb_float_new)

な感じかな?もうちょい C を勉強し直さないとダメだなorz.

###コンパイルと実行 今回は外部ファイルが必要なので,その記述も含めて extconf.rb を書きます.

  • extconf.rb
require 'mkmf'

dir_config('narray', $sitearcchdir, $sitearcchdir)

if !(have_header('narray.h') and have_header('narray_config.h'))
  print <<-EOS
  ** configure error **
  Header narray.h or narray_config.h is not found. If you have these files in /narraydir/include, try the following:

  % ruby extconf.rb --with-narray-include=/narraydir/include

  EOS
  exit -1
end

if /cygwin|mingw/ =~ RUBY_PLATFORM
  unless have_library('narray')
    print <<-EOS
    ** configure error **
    libnarray.a is not found.
    % ruby extconf.rb --with-narray-lib=/narraydir/lib

    EOS
    exit -1
  end
end

create_makefile('test')

そしてコンパイルと実行

% ruby extconf.rb --with-narray-include=/var/lib/gems/1.8/gems/narray-0.5.9.6
% make
% irb
>> require 'narray'
=> true
>> require 'test'
=> true
>> a = NArray.sfloat(5).indgen!(1,1)
=> NArraysfloat5:
[ 1.0, 2.0, 3.0, 4.0, 5.0 ]
>> Test.mul_all(a)
=> 120.0

###NArray へのメソッド追加 次は Ruby らしく,NArray#mul_all として追加してみることに.

  • test4.c
#include "ruby.h"
#include "narray.h"

float mul_all(float array[], int nx);

VALUE wrap_mul_all(self)
     VALUE self;
{
  VALUE na2;
  struct NARRAY *n_na;
  float result;

  na2 = na_cast_object(self, NA_SFLOAT);
  GetNArray(na2, n_na);
  result = mul_all((float*)n_na->ptr, n_na->total);
  return( rb_float_new(result) );
}

void Init_test()
{
  VALUE class_na;

  rb_require("narray");
  class_na = rb_const_get(rb_cObject, rb_intern("NArray"));
  rb_define_method(class_na, "mul_all", wrap_mul_all, 0);
}

基本的にさっきと同じようだが,

  1. メソッドが実行されている VALUE 型構造体(self)を,NArray オブジェクト(na2)へ cast する (na_cast_object)

  • NArray クラスを取得する (rb_const_get)
  • NArray クラス(class_na) に mul_all メソッドを追加する (rb_define_method)

と言う点が異なる.

そしてコンパイルと実行.

% mv test3.c test3.c.org
% ruby extconf.rb --with-narray-include=/var/lib/gems/1.8/gems/narray-0.5.9.6
% make
% irb
>> require 'narray'
=> true
>> require 'test'
=> true
>> NArray.sfloat(5).indgen!(1,1).mul_all
=> 120.0
>>

何となく流れはわかったんだけど,もっと C の知識が必要だなーと,ちょっと凹んだ・・・・

 
comments powered by Disqus