LoginSignup
2
2

More than 5 years have passed since last update.

Rubyがどんな風に動いてるのか調べてみた(Fixnum,"+",fix_plus)その①

Last updated at Posted at 2016-05-11

Ruby歴が1年くらいのエンジニアです!
最近ようやくRubyがどういう風に動いてるのかに興味が出てきたので、Rubyのコア部分のコードを頑張って読んでみる事にしました。
最終的には、コミット出来るところまでいけたら最高だなと思っています。(不定期更新です)

準備

現在の安定版は 2.3.1のようなので、2.3.1で勉強していきたいと思います。

ソースコードのダウンロードはこちらから
https://www.ruby-lang.org/ja/downloads/

Documentはこちらを参考にしたり、ググったりです
http://docs.ruby-lang.org/ja/2.3.0/class/Object.html

第一印象

意気揚々とダウンロードして読み始めたのですが

という感じです笑
なので、本当にのんびりな進みになると思うので、生温い目で見守っていてください笑

足がかり

辛さを感じながら、Cのソースコードを読んで行くと

rb_define_method

という、いかにもmethodを定義してそうなコードを見つけました。
これだ!と想い、git grep "rb_define_method"で検索してみると、出てくる出てくる!

スクリーンショット 2016-05-11 13.51.30.png

見た事ある単語も出てきて、だいぶ安心しました笑

webで検索しても出てきました。
http://rurema.clear-code.com/query:rb_define_method/version:2.3.0/

ただしくは
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(), int argc)が正しいらしいです。

  • klassがメソッドが所属するクラス
  • *nameが、インスタンスメソッドの名前
  • *funcが、どんな挙動をするか
  • argcが、メソッドの引数の数

のようです!
これがわかったところで簡単そうなメソッドを読んでみます

Numeric.cのfix_plusについて読んでみる

今回は以下のメソッドについて深めて行きたいと思います。
rb_define_method(rb_cFixnum, "+", fix_plus, 1);

まず、numeric.c内でrb_cFixnumで検索をかけるとたくさん出てきます。

スクリーンショット 2016-05-11 14.01.03.png

で、これを見てみると、

rb_cFixnum = rb_define_class("Fixnum", rb_cInteger);というまたまたいかにも、クラスを定義してそうなコードを発見しました。

ここに書かれている、rb_cIntegerを検索すると

rb_cInteger = rb_define_class("Integer", rb_cNumeric);

が出てきました。
どんどん、クラスが継承されていっているみたいです。

さらに、掘って行きます。

rb_cNumeric = rb_define_class("Numeric", rb_cObject);

が出てきました。
で、rb_cObjectを調べるとobject.cファイルの3378行目にありました。

rb_cObject = rb_define_class("Object", rb_cBasicObject);

で、rb_cBasicObjectを調べると

rb_cBasicObject = rb_define_class("BasicObject", Qnil);

Qnilってなんだ!!

と思ったら、
ここのWEBサイトにかいてありました。
http://ruby.gfd-dennou.org/tutorial/ruby-ext/

Rubyから直接呼び出す関数は戻り値としてRubyのオブジェクトを返す 必要があります。 今は特に何も戻り値が必要でないので、Rubyの「nil」に 対応する「Qnil」という定数を与えています。

というわけで、rb_cBasicObjectが一番元のクラスだという事が判明しました。

階層的には

rb_cBasicObject

rb_cObject

rb_cNumeric

rb_cInteger

rb_cFixnum

という、継承構造のようです。

本題のfix_plusの方を調べて行きたいと思います。

rb_define_method(rb_cFixnum, "+", fix_plus, 1);

rb_cFixnumというクラスに、+という名前で、fix_plusの挙動を定義します。で、引数はひとつです。

では、fix_plusの挙動について調べましょう。

numeric.c
static VALUE
fix_plus(VALUE x, VALUE y)
{
    if (FIXNUM_P(y)) {
    long a, b, c;
    VALUE r;

    a = FIX2LONG(x);
    b = FIX2LONG(y);
    c = a + b;
    r = LONG2NUM(c);

    return r;
    }
    else if (RB_TYPE_P(y, T_BIGNUM)) {
    return rb_big_plus(y, x);
    }
    else if (RB_TYPE_P(y, T_FLOAT)) {
    return DBL2NUM((double)FIX2LONG(x) + RFLOAT_VALUE(y));
    }
    else if (RB_TYPE_P(y, T_COMPLEX)) {
    VALUE rb_nucomp_add(VALUE, VALUE);
    return rb_nucomp_add(y, x);
    }
    else {
    return rb_num_coerce_bin(x, y, '+');
    }
}

引数2つじゃね?とおもったのですが、0が1で、1が2的なあれかなと、とりあえず思って今は流しておきたいと思います。判明したら、正しく書きます。

+だから簡単かなと思ったら、意外とたくさん条件分岐があって面食らっています笑

FIXNUM_P(y)とは!?

検索したらでてきました。関数の用です(version違うけどきっと同じだと思われます。)
http://docs.ruby-lang.org/ja/1.9.3/function/FIXNUM_P.html

というわけで、yがFIXNUMだとtrueが返るようです。

yがFIXNUMの場合

FIX2LONGこの関数は見た目からして、型をFIXNUMをLONGにするやつっぽいのでそういう認識で活きます。

LONG2NUMこの関数も、型をLONGからNUMに返る奴でしょう。

まとめ

このメソッドは
rb_define_method(rb_cFixnum, "+", fix_plus, 1)

Fixnumというクラスに対して、+が呼ばれた時に

引数yがFIXNUMの時、
引数xと引数yをまず、LONG型に変換し、足し算してから、返り値として、NUM型のrを返すというメソッドを定義する

という、もののようです。

実際に読んでみると意外なんとかなるなという印象です。(これであっていれば笑)
きっと難しいところはめっちゃ難しいんだと思いますがちょっとずつ頑張って行こうと思います。

2
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2