2013年3月26日火曜日

value.h (その3〜 not MRB_NAN_BOXING 〜 構造体と共用体を複合したサブクラスの実現)

mrb_value では,構造体と共用体を複合させることで,複数種類のサブクラスに共通のデータフォーマットを与えています。MRB_NAN_BOXING が偽の場合(not MRB_NAN_BOXING)について説明します。

図1 mrb_value の PIM
図1は mrb_value の PIM をちょっと整理しなおしています。mrb_value のサブクラスの中には値となる属性を持つものがあります。たとえば,MRB_TT_FLOAT (浮動小数点数) , MRB_TT_VOIDP (ポインタ), MRB_TT_FIXNUM (整数) , MRB_TT_SYMBOL (シンボル) です。他のサブクラスも値を持つ場合がありますし,持たない場合もあるようです。

重要な前提がいくつかあります。
  1. まず mrb_value は型を1つだけ持ちます。この場合の型というのは,例の接頭辞 MRB_TT で始まる25種類の列挙型で表される情報のことです。mrb_value が型をもたなかったり,逆に2つ以上の型を持つことはありません。
  2. 次に mrb_value は値を高々1つ持ちます。「高々1つ」というのは,「多くとも1個」すなわち「0個または1個」という意味です。
  3. 型によって値の参照のしかたが一意に決まります。たとえば型が MRB_TT_FLOAT だった場合には値は mrb_float 型の f を参照します。同様にたとえば型が MRB_TT_VOIDP だったときには値は void * 型の p を参照します。
こういう条件を満たす場合の C 言語での実装方法として,構造体と共用体を併用する方法が一般的に用いられます。UML にするとこんな感じです。
図2 mrb_value の PSM
C言語ではこんな感じです(実際のプログラムから少し簡素化しています)。

struct mrb_value {
  union {
    mrb_float f;
    void *p;
    mrb_int i;
    mrb_sym sym;
  } value;
  enum mrb_vtype tt;
}

共用体 (union) は,中括弧 { } で囲まれた変数を同じメモリ領域に配置します。この例だと,mrb_float f と void *p と mrb_int i と mrb_sym sym の4つの変数を1箇所のメモリ領域に強引に配置します。
前提1と前提3により,同じメモリ領域に配置しても混乱することはありません。なぜならば,いったん型が決まってしまったあとは値の参照方法も決まってしまうので,異なる値の参照方法を同時に使うことはないからです。前提2により,mrb_value 1つに対し,共用体 value で確保するメモリ領域は1つだけあれば十分です。
この共用体 value と型を表す tt の2つを構造体 (struct) としてまとめておきます。このようにしておくと,たとえば次のようにして mrb_value から情報を読み書きできます。
  • 変数 v の型を参照する: v.tt
  • 整数 a を格納する: v.tt = MRB_TT_FIXNUM; v.value.i = a;
  • ポインタ ptr を格納する: v.tt = MRB_TT_VOIDP; v.value.p = ptr;

2013年3月19日火曜日

value.h (その2〜 PIM)

http://mruby-complete.blogspot.jp/2013/03/value-class.html の続きです。

では,value.h で定義されている構造体 mrb_value は,どのようなことを実現しようとしているのでしょうか。図1に私の解釈を示します。

図1 mrb_value の PIM
上記のクラス図の ... で省略されているのは,元のソースコードで列挙型 mrb_vtype として表現されていた次のような情報です。

  • MRB_TT_FALSE
  • MRB_TT_FREE
  • MRB_TT_TRUE
  • MRB_TT_FIXNUM 
  • MRB_TT_SYMBOL
  • MRB_TT_UNDEF
  • MRB_TT_FLOAT
  • MRB_TT_VOIDP 
  • MRB_TT_MAIN
  • MRB_TT_OBJECT
  • MRB_TT_CLASS
  • MRB_TT_MODULE
  • MRB_TT_ICLASS
  • MRB_TT_SCLASS
  • MRB_TT_PROC
  • MRB_TT_ARRAY
  • MRB_TT_HASH
  • MRB_TT_STRING
  • MRB_TT_RANGE
  • MRB_TT_STRUCT
  • MRB_TT_EXCEPTION
  • MRB_TT_FILE
  • MRB_TT_ENV 
  • MRB_TT_DATA
  • MRB_TT_MAXDEFINE

これらは mruby で扱う値の型を表現しています。それぞれの型に対応したデータ形式で実際の値が格納されます。抽象クラス mrb_value は,これらのデータを包括的に操作するインタフェースを提供します。

一方,実際のソースコードを読み解くと,前回の記事に示したようにもっと複雑な構造をしています。このように複雑になる理由は,主にプログラミング言語の制約のためです。このように,シンプルに意図を表現したモデルと,プログラミング言語に合わせて特化したモデルと,少なくとも2通りの異なるモデルが存在します。MDA (Model-Driven Architecture) という技法では,これらを次のように呼んでいます。

  • PIM (Platform Independent Model): プログラミング言語などのプラットフォームに依存しない,設計意図を表現したモデル
  • PSM (Platform Specific Model): プログラミング言語などのプラットフォームに合わせて特化したモデル

2013年3月9日土曜日

value.h (その1〜クラス図)

 mruby の value.h をクラス図にしました。
とても読み応えありました。勉強になりました。取り急ぎ,今日のところは説明抜きでクラス図をご覧ください。
図1 mrb_value, mrb_vtype
図2 RBasic, RObject, MRB_OBJECT_HEADER

2013年3月6日水曜日

C言語のデータ構造をUMLのクラス図で表す方法

class.h の RClass を例にC言語のデータ構造を UML のクラス図で表す方法について説明します。

ソースコードには次のように書かれています。


struct RClass {
  MRB_OBJECT_HEADER;
  struct iv_tbl *iv;
  struct kh_mt *mt;
  struct RClass *super;
};


ひとまず RClass というクラスが存在すると考えましょう(図1)。

図1 struct RClass を UML クラス図に書く過程 その1


MRB_OBJECT_HEADER の実体は value.h で定義されていますが,ここでは属性の1つだなというぐらいに思うことにしましょう(図2)。
図2 struct RClass を UML クラス図に書く過程 その2

次の struct iv_tbl *iv; は別の構造体へのポインタです。構造体へのポインタが現れた場合には,次のような手順でクラス図を書きます。

  1. ある構造体へのポインタが現れたら,その構造体を表すクラスとの間に片方向の関連の線を引きます。
    • その構造体を表すクラスが既出の場合には関連線を追加するだけです。
    • 未出の場合には対応するクラスを新たに作って関連線を引きます。
  2. あとで登場した方の構造体を表すクラスの側の関連端のロール名を,元の構造体のメンバー変数名とします。
  3. あとで登場した方の構造体を表すクラスの側の関連端の多重度を,そのメンバー変数の個数とします。
RClass の struct iv_tbl *iv; について説明します。
  1. ある構造体へのポインタ (struct iv_tbl *iv; ) が現れたので,その構造体 (iv_tbl) を表すクラス iv_tbl との間に片方向の関連の線を引きます。
    • この場合は,未出の場合なので,対応するクラス(iv_tbl)を新たに作って関連線を引きます(図3)。
  2. あとで登場した方の構造体(iv_tbl)を表すクラスの側の関連端のロール名を,元の構造体(RClass)のメンバー変数名(iv)とします(図4)。
  3. あとで登場した方の構造体(iv_tbl)を表すクラスの側の関連端の多重度を,そのメンバー変数の個数(1)とします(図5)。
図3 struct RClass を UML クラス図に書く過程 その3

図4 struct RClass を UML クラス図に書く過程 その4

図5 struct RClass を UML クラス図に書く過程 その5

この時の注意点としては,あとで登場した方の構造体を表すクラスから,元の構造体を表すクラスへの矢印や関連端のロール名,多重度がどうなっているかは,ソースコードを詳しく分析しないとわからないという点です。
たとえば,この例だと iv_tbl -> RClass の向きの関連が存在するかどうかは, iv_tbl の構造体を定義しているソースコードを見ないとわかりません。
さらに言えば,もし iv_tbl -> RClass となる関連が存在したとしても,その関連と,先ほど作成した iv の関連が対になるかどうかは,すべての関数のソースコードを読んで,対になるように一貫して管理されているかどうかを確認する必要があります。

続きを読み進めていきましょう。 struct kh_mt *mt; については先ほどと同じなので,わかりますよね? (図6)

図6 struct RClass を UML クラス図に書く過程 その6

その次の struct RClass *super; については次のような感じになります。

  1. ある構造体へのポインタ (struct RClass *super;) が現れたら,その構造体 (RClass) を表すクラスとの間に片方向の関連の線を引きます。
    • その構造体(RClass) を表すクラスが既出の場合なので,関連線を追加するだけです。自分自身への関連である(自己参照している)点に注意してください(図7)。
  2. あとで登場した方の構造体 (RClass) を表すクラスの側の関連端のロール名を,元の構造体 (RClass) のメンバー変数名 (super) とします(図8)。
  3. あとで登場した方の構造体 (RClass) を表すクラスの側の関連端の多重度を,そのメンバー変数の個数 (1) とします(図9)。
図7 struct RClass を UML クラス図に書く過程 その7

図8 struct RClass を UML クラス図に書く過程 その8
図9 struct RClass を UML クラス図に書く過程 その9



RClass (クラス図)

Ruby はオブジェクト指向プログラミング言語(OOPL)なので,クラスをどのように実現しているかを最初に読むのが良かろうと判断しました。メソッド(関数)がいっぱい定義されていますが,まずは静的構造から読み始めました。 class.h に定義されているクラス(構造体),RClass 周辺のクラス図を書きました。
C言語の構造体は OOPL のクラスに,C言語の関数は OOPL のメソッドに対応すると考えます。下記のクラス図で現れるクラスは,実際にはC言語の構造体で表されています。

RClass 周辺の(メタ)クラス図


RClass は mruby のクラスを実現するもので,おそらく Ruby Class の略なのでしょう。
RClass 自身への関連 super はスーパークラスを表現するものです。
RClass には他に iv と mt の2つの関連があります。おそらく iv は instance variable,つまりインスタンス変数(属性)を表していて,mt はメソッド(操作)なのでしょうね。
MRB_OBJECT_HEADER の本体は value.h で定義されています。「mruby のオブジェクトに付属するヘッダー」というような意味合いなのでしょう。

iv_tbl は「インスタンス変数テーブル」でしょうね。rootseg でつながっている segment がリスト構造になっています。size と last_len はリスト構造の長さに関わる変数だと思いますが,使い分けはソースコードをよく読まないとわかりません。

segment に実際のインスタンス変数のメタ情報が格納されるようです。key, val という変数名なので,連想配列のイメージを持てばよさそうです。

mrb_sym は mrbconf.h で定義されていて,実体は short になっています。「mrubyのシンボル」という意味でしょう。short 型ではありますが,mrb_sym を操作する専用の関数が定義されているようなので,クラスとして扱ってもいいのかもしれません。
mrb_value は value.h で定義されていて,実体は共用体(union)を含む構造体になっています。

kh_mt はメソッドを格納するのだと思いますが,実体はどこにも定義されていないように思います。メソッドを詳しく読まないとわからなそうです。

最後の fc_result は,現時点ではどんな役割を担っているのか,想像つきません。ここでも mrb_sym と mrb_value が登場します。

どうやら mrb_sym と mrb_value は mruby の基本的なデータ構造のようなので,次回はこれらを読むことにしようと思います。

2013年2月21日木曜日

環境設定



まずは環境設定からはじめました。

HomeBrew のほうが Ruby と相性がよさそうなので,MacPorts から移行しました。

MacPorts のアンインストール
http://tech.tmd45.in/entry/2012/07/05/120858
ただし行うのは MacPorts のアンインストールまでです。

ここで,Xcode を最新版 (現時点では Xcode 4.6) にアップグレードしておきましょう。

HomeBrew のインストール方法は現時点の Mountain Lion では次のとおりです。
http://tools4hack.santalab.me/howto-mountainlion-install-homebrew.html

エディタとして TextMate 2 をインストールしちゃいました。
https://github.com/textmate/textmate
インストール方法は英語ですがきちんと書いてあります。大して難しくないですよ。

途中,MacPorts を使っている次の手順は,HomeBrewを使うように改めます。

sudo port install ninja ragel boost multimarkdown mercurial
brew install ninja ragel boost multimarkdown mercurial

Emacs を使いたい人は,次のコマンド。

brew install --cocoa emacs
sudo ln -s /usr/local/Cellar/emacs/24.2/Emacs.app /Application/

いよいよ,おもむろに mruby をインストール
https://github.com/mruby/mruby

  1. README.md を読んで,How to get mruby の手順に従う。
  2. INSTALL を読んで Prerequisites を満たしたあと,Compilation and Installation の手順に従う。
以上,ここまでは簡単ですね。
さて,どこから読むんだ? まぁでも,お楽しみにしておこうっと。