[ruby-dev:50476] オブジェクトの符号無し整数型への変換について
From:
"t. Kad" <pute.tk@...>
Date:
2018-02-16 19:09:15 UTC
List:
ruby-dev #50476
はじめまして。
ruby-list に投稿すべきか dev に投稿すべきか迷ったのですが、
C APIの実装に関することなのでこちらかなと思って投稿します。
* 問題
NUM2Uxxxの挙動がドキュメントに従っていない。
ドキュメントでは符号無し整数で表せない負数が渡されると
RangeError を発生させることになっているが、
実際には wrap around した正数が返ってくる。
ドキュメントのとおりに例外を投げるべきでは?
※細かい変更のくせに実装を変更する影響が大きいので
誰も見ないふりをしてる(ように見える)。
* 背景
関連するMLのスレッドは以下です。
- NUM2UINT does not rase RangError for negative Fixnum - noreply@ b o
e r (2005/04/23)
http://blade.nagaokaut.ac.jp/cgi-bin/vframe.rb/ruby/ruby-core/4753?4748-5698
特に進捗なし
- NUM2xxx - Tanaka Akira (2008/02/10)
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/33683
まつもとさんの発言:
> NUM2Uxxxが負の値を受けいれるのは、なんか理由があったような気がする
その後特に進捗なし?
* 提案
1. RangeError を発生するよう実装を変更する
- メリット:ドキュメント通りの動作になり、マクロ名からも想定可能な動作になる
- デメリット:影響範囲が広すぎて修正が大変・管理されていない外部ライブラリを切り捨てることになる
2. ドキュメントを実装にあわせる
- メリット:実質的に何もしなくていい・既存の動作に影響を与えない(どうせ各種ライブラリも現実装にあわせて作られてるし……)
- デメリット:無責任
2-1. 負数を受け取ったら例外を投げる別のマクロ/関数を作る
- メリット:既存の動作に影響を与えない・いちいち引数チェックするのは面倒だから本体で面倒を見てくれるとわりと嬉しい
- デメリット:特に思いつきません
というわけで、現状の check_uint(unsigned long num, int sign) [numeric.c] に加えて
以下の check_uint_strict(unsigned long num, int sign) みたいなのを作ってやれば
いいのではないか……というのはまあ素人考えなのですが、いかがでしょうか?
少なくともドキュメントと挙動が乖離している今の状況は改善すべきではないかと思います。
===
--- numeric.c 2018-02-17 03:50:16.401771000 +0900
+++ numeric.c 2018-02-17 04:02:42.365136100 +0900
@@ -2859,6 +2859,20 @@
rb_raise(rb_eRangeError, "integer %lu too big to convert to
`unsigned int'", num);
}
}
+
+static void
+check_uint_strict(unsigned long num, int sign)
+{
+ if (sign) {
+ /* minus */
+ rb_raise(rb_eRangeError, "integer %ld too small to convert to
`unsigned int'", (long)num);
+ }
+ else {
+ /* plus */
+ if (UINT_MAX < num)
+ rb_raise(rb_eRangeError, "integer %lu too big to convert to
`unsigned int'", num);
+ }
+}
long
rb_num2int(VALUE val)
===
pute
pute.tk@gmail.com