[ruby-core:78347] [CommonRuby Feature#12732] An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exception
From:
naruse@...
Date:
2016-11-25 09:34:58 UTC
List:
ruby-core #78347
Issue #12732 has been updated by Yui NARUSE.
Below is PoC; it may have a path which raises an exception.
```diff
diff --git a/object.c b/object.c
index 05bef4d..5d63803 100644
--- a/object.c
+++ b/object.c
@@ -2750,17 +2750,60 @@ static VALUE
rb_f_integer(int argc, VALUE *argv, VALUE obj)
{
VALUE arg = Qnil;
+ VALUE opts = Qnil;
+ VALUE exception = Qnil;
+ VALUE vbase = Qundef;
int base = 0;
+ static ID int_kwds[1];
- switch (argc) {
- case 2:
- base = NUM2INT(argv[1]);
- case 1:
- arg = argv[0];
- break;
- default:
- /* should cause ArgumentError */
- rb_scan_args(argc, argv, "11", NULL, NULL);
+ rb_scan_args(argc, argv, "11:", &arg, &vbase, &opts);
+ if (!NIL_P(vbase)) {
+ base = NUM2INT(vbase);
+ }
+ if (!NIL_P(opts)) {
+ if (!int_kwds[0]) {
+ int_kwds[0] = rb_intern_const("exception");
+ }
+ if (rb_get_kwargs(opts, int_kwds, 0, 1, &exception)) {
+ VALUE tmp;
+ if (RB_FLOAT_TYPE_P(arg)) {
+ double f;
+ if (base != 0) goto arg_error;
+ f = RFLOAT_VALUE(arg);
+ if (FIXABLE(f)) return LONG2FIX((long)f);
+ return rb_dbl2big(f);
+ }
+ else if (RB_INTEGER_TYPE_P(arg)) {
+ if (base != 0) goto arg_error;
+ return arg;
+ }
+ else if (RB_TYPE_P(arg, T_STRING)) {
+ const char *s;
+ long len;
+ rb_must_asciicompat(arg);
+ RSTRING_GETMEM(arg, s, len);
+ tmp = rb_cstr_parse_inum(s, len, NULL, base);
+ if (NIL_P(tmp)) {
+ return exception;
+ }
+ return tmp;
+ }
+ else if (NIL_P(arg)) {
+ if (base != 0) goto arg_error;
+ return exception;
+ }
+ if (base != 0) {
+ tmp = rb_check_string_type(arg);
+ if (!NIL_P(tmp)) return rb_str_to_inum(tmp, base, TRUE);
+arg_error:
+ rb_raise(rb_eArgError, "base specified for non string value");
+ }
+ tmp = convert_type(arg, "Integer", "to_int", FALSE);
+ if (NIL_P(tmp)) {
+ return rb_to_integer(arg, "to_i");
+ }
+ return tmp;
+ }
}
return rb_convert_to_integer(arg, base);
}
```
```ruby
def assert(a, b)
if a != b
raise "'#{a}' != '#{b}'"
end
end
def assert_raise(ex)
begin
yield
raise "#{ex} is expected but not raised"
rescue ex
# correct
rescue
raise "#{ex} is expected but #{$!.inspect}"
end
end
o = Object.new
assert 123, Integer("123")
assert 50, Integer("32", 16)
assert 16, Integer("10", 16, exception: o)
assert o, Integer("x", exception: o)
assert o, Integer("x", 16, exception: o)
assert_raise(ArgumentError){ Integer("x") }
assert_raise(ArgumentError){ Integer("x", 16) }
```
```ruby
require'benchmark/ips'
Benchmark.ips{|x|
x.report("rescue") {
Integer('foo') rescue nil
}
x.report("kwarg") {
Integer('foo', exception: nil)
}
}
```
```
Warming up --------------------------------------
rescue 36.258k i/100ms
kwarg 64.004k i/100ms
Calculating -------------------------------------
rescue 392.926k (賊 8.9%) i/s - 1.958M in 5.025204s
kwarg 844.563k (賊14.9%) i/s - 4.096M in 5.017539s
```
----------------------------------------
Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exception
https://bugs.ruby-lang.org/issues/12732#change-61713
* Author: Aaron Patterson
* Status: Feedback
* Priority: Normal
* Assignee: Yukihiro Matsumoto
----------------------------------------
I would like to be able to pass an option to `Integer()` and `Float()` so that they don't raise an exception, but return `nil` instead. For example:
~~~
Integer(string, exception: false)
~~~
The reason I want this function is so that I can convert strings from YAML or JSON to integers if they parse correctly, or just return strings if they can't be parsed.
---Files--------------------------------
integer-parse.pdf (29 KB)
--
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>