Ruby

TIPS 〜 外界闊歩編

SOFTWARE] - [SOFTWARE.ruby] - [Ruby] - [ルートメニュー
  1. 拡張モジュールからグローバル変数を参照
  2. 見つけにくいバグ



目次に戻る
1. 拡張モジュールからグローバル変数を参照
(確認) ruby 1.6.4

 拡張モジュールのドキュメントを読んでも、グローバル変数にアクセスするための方法は書かれていません。インスタンス変数や、定数へのアクセス方法は書かれているのですけれどもね。もしかして拡張モジュールからグローバル変数は参照できないのか!? なんて思うかもしれませんが、よ〜く探してみると、ちゃんとそのための関数が提供されています。
 その関数とは rb_gv_get( name ) です。引数 name には、グローバル変数文字列を渡します。先頭の $ がない場合は自動的に付加されますが、ソースを読むと新たな文字列オブジェクトを作成しているようですので、特別な理由がなければ、ちゃんと $ を書いておきましょう。

    waiting = NUM2INT( rb_gv_get( "$Start" ) );

 上が実際に使っている場面です。(C 言語ですよ、もちろん。)
 グローバル変数を宣言する方法、というのも書かれているのですけれどねえ。ちなみに、rb_gv_set という関数もあります。




目次に戻る
2. 見つけにくいバグ
(確認) ruby 1.6.4

 すでに周知のことだと思いますが、Ruby においては、メモリブロックが数バイトごとに確保されることを利用してオブジェクトと数値の区別を行っています。最下位ビットが 1 の時は数値、0 のときはポインタであると認識するのです。そうそう、Ruby では数値をポインタに格納します。ですので、long のサイズとポインタのサイズが異なる環境ではビルドできないそうです。
 さて、この事がちょっとした見つけにくいバグを招いてしまうことがあります。たとえば、次のコードを見てください。

    size = rb_funcall( obj, rb_intern( "size" ), 0 );

 パッと見ると、正しそうに見えます。が、実はこれは間違いで、正しくは次のようにします。

    size = FIX2INT( rb_funcall( obj, rb_intern( "size" ), 0 ) );

 NUM2INT でもかまいませんが、普通は size メソッドを呼び出すと適当な大きさの数値が返ってきますから、こっちの方が高速でいいでしょう。安全性をとるのならば NUM2INT ですが。
 たとえば 0 が返ってきてほしいところでは 1 が返ってきます。1 が返ってきてほしいところでは 3 になってしまいます。
 理由はわかりますね? 最下位ビットがセットされているからです。ちゃんと変換を行わないままに Ruby の返値を使うと、思わぬところで値が合わないなどのバグと遭遇してしまうことになります。変換作業はちゃんとやりましょう。また、変な値になっているときはまず「変換を忘れていないか?」と疑ってみるべきです。




SOFTWARE] - [SOFTWARE.ruby] - [Ruby] - [ルートメニュー