1. jkr_2255

    Posted

    jkr_2255
Changes in title
+Ruby 2.4でIntegerに一本化される整数
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,47 @@
+Ruby 2.4から、整数のクラスが付け変わることになりますが、特にCエクステンションを書いている場合は注意の必要があります。
+
+# Ruby 2.3までの世界
+Rubyの世界にももちろん整数がありますが、(Ruby側から見て)今までは抽象クラスの`Integer`の下に、絶対値の小さな整数については`Fixnum`、それに収まらないものについては`Bignum`という具象クラスがある、というように分かれていました。このあたりについては、[以前書いた記事](http://qiita.com/jkr_2255/items/476a4a82a0a2699ad777)が参考になるかと思います。
+
+## 問題点
+このような形にしていることで、いくつか問題がありました。
+
+* `Fixnum`と`Bignum`は実装の詳細の違いで、Ruby側から違いが見える必然性はない
+ * 同じ整数なのに`Fixnum`と`Bignum`で挙動を違える、なんてことはむしろ**やるべきでない**
+* 環境によって`Fixnum`のサイズが違う(可搬性に乏しい)
+* RubyのISO規格上も、`Integer`以下は実装依存となっている
+
+# Ruby 2.4での変更点
+そんなこともあって、Ruby 2.4からは両者が`Integer`へ統合されることになりました。
+
+## Ruby側から見て
+ふつうにプログラムを書いている中では、そもそも`Fixnum`や`Bignum`を直接使う場面自体が少なかったでしょうし、そこまで大問題とはならないと思いますが、いくつか注意すべき点はあります。
+
+既存のプログラムとの互換上、`Fixnum`と`Bignum`という定数は存在し続けますが、どちらも**`Integer`のエイリアス**となります。それぞれをオープンクラスなどしていた場合も、直接`Integer`を変更するような形となります(きちんと対応する形に修正した方がいいでしょう)。
+
+そして、`foo.is_a?(Fixnum)`のように`Fixnum`に収まるかチェックしていた場合、Ruby 2.4では`foo.is_a?(Integer)`とみなされて、整数であれば常にtrueとなります。(Cエクステンションが絡むような状況なら別として)`Fixnum`かどうかをチェックするという意図自体が根本的に問題なので、きちんと範囲チェックするように修正しましょう。
+
+あと、これは実際に影響する場面もあるかもしれませんが、`Fixnum`や`Bignum`という定数そのものをDSLの一部に使っていた場合、`Integer`と同じ値になってしまうのでうまく動かなくなります。使っているgemの説明に従って、適切に置き換えましょう。
+
+## C言語側から見て
+Ruby上では`Fixnum`と`Bignum`の区別は廃止されましたが、C言語の世界では2種類の内部構造がそのまま存続する形となっています。ということで、「整数を扱う部分をすべて書き換える必要がある」なんてことにはなりません。
+
+ただし、C言語上では`Fixnum`と`Bignum`を表す`rb_cFixnum`と`rb_cBignum`が**廃止されています**。これらの定数を使っていた場合、コンパイルエラーとなります。代替策としては、
+
+* 型チェックに使っていた場合→`FIXNUM_P`[^1]や`RB_TYPE_P`などで置き換えましょう。内部データ構造を示すための`T_BIGNUM`は健在です。
+* メソッド定義などをしていた場合→`Integer`(`rb_cInteger`)にセットする形に切り替えましょう。`Fixnum`用と`Bignum`用の関数が別にある場合、内部で振り分けする必要が出てきます。
+
+また、これらの振り分けを支援するための定数として、`RUBY_INTEGER_UNIFICATION`が定義されています。`#ifdef RUBY_INTEGER_UNIFICATION`で切り分けましょう。
+
+## 自作gemのチェック
+以前に作っていた[jkr2255/bit_utils](https://github.com/jkr2255/bit_utils)([解説](http://qiita.com/jkr_2255/items/d0cd5478d64da2f1b450))について、コードをチェックしてみましたが、内部的には`Fixnum`用と`Bignum`用のメソッドがあって、
+
+* `Fixnum`用…`Bignum`を投げるとエラーになる
+* `Bignum`用…`Fixnum`でも動くけど、少し余分な処理が入る
+
+というようになっていました。そして、`Fixnum`をオープンして使うとき以外は`Bignum`用をそのまま使うようになっていて、オープンクラス時も`Fixnum`→`Bignum`の順で行っていたので、そのままですべてがうまくいくような流れになっていました。Ruby 2.4.0-preview2でも正常に動作しました。
+
+とはいえ、`Fixnum`と`Bignum`を両方オープンしているのも行儀が悪いので、書き換えを行う予定です。
+
+
+[^1]: 1ビットを見るだけなので、きわめて高速です。