Ruby iterators now automatically return an Enumerator if no block is given - allows very cool code ..

This commit is contained in:
Matthias Koefferlein 2020-08-31 23:09:37 +02:00
parent a5d675304c
commit 4c127b4644
4 changed files with 50 additions and 4 deletions

View File

@ -499,6 +499,16 @@ public:
a = A::new a = A::new
a.each { |i| ... }</pre> a.each { |i| ... }</pre>
<p>
If no block is given, an Enumerator object is created. Enumerators
are a Ruby feature. Enumerators support many convenient methods
like sort, inject, collect, select etc. Here is an example:
</p>
<pre>
# turns all elements returned by the iterator into strings and sorts them
sorted = a.each.collect(&amp;:to_s).sort</pre>
<p> <p>
Iterators match very well between C++ and Ruby so there are no real issues here. Iterators match very well between C++ and Ruby so there are no real issues here.
The return type of the iterators is mapped to Ruby's block arguments using the The return type of the iterators is mapped to Ruby's block arguments using the

View File

@ -89,7 +89,8 @@ public:
{ {
std::vector<tl::BacktraceElement> bt; std::vector<tl::BacktraceElement> bt;
bt.push_back (tl::BacktraceElement (rb_sourcefile (), rb_sourceline ())); bt.push_back (tl::BacktraceElement (rb_sourcefile (), rb_sourceline ()));
rba_get_backtrace_from_array (rb_funcall (rb_mKernel, rb_intern ("caller"), 0), bt, 0); static ID id_caller = rb_intern ("caller");
rba_get_backtrace_from_array (rb_funcall (rb_mKernel, id_caller, 0), bt, 0);
return bt; return bt;
} }
@ -113,7 +114,8 @@ public:
// But the purpose is a relative compare, so efficiency is not sacrificed here // But the purpose is a relative compare, so efficiency is not sacrificed here
// for unnecessary consistency. // for unnecessary consistency.
int d = 1; int d = 1;
VALUE backtrace = rb_funcall (rb_mKernel, rb_intern ("caller"), 0); static ID id_caller = rb_intern ("caller");
VALUE backtrace = rb_funcall (rb_mKernel, id_caller, 0);
if (TYPE (backtrace) == T_ARRAY) { if (TYPE (backtrace) == T_ARRAY) {
d += RARRAY_LEN(backtrace); d += RARRAY_LEN(backtrace);
} }
@ -933,16 +935,18 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor)
if (p) { if (p) {
static ID id_set = rb_intern ("set");
VALUE signal_handler = p->signal_handler (meth); VALUE signal_handler = p->signal_handler (meth);
if (rb_block_given_p ()) { if (rb_block_given_p ()) {
VALUE proc = rb_block_proc (); VALUE proc = rb_block_proc ();
RB_GC_GUARD (proc); RB_GC_GUARD (proc);
ret = rba_funcall2_checked (signal_handler, rb_intern ("set"), 1, &proc); ret = rba_funcall2_checked (signal_handler, id_set, 1, &proc);
} else if (argc > 0) { } else if (argc > 0) {
ret = rba_funcall2_checked (signal_handler, rb_intern ("set"), argc, argv); ret = rba_funcall2_checked (signal_handler, id_set, argc, argv);
} else { } else {
ret = signal_handler; ret = signal_handler;
} }
@ -991,6 +995,24 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor)
p->set (obj, true, false, true, self); p->set (obj, true, false, true, self);
} }
} else if (meth->ret_type ().is_iter () && ! rb_block_given_p ()) {
// calling an iterator method without block -> deliver an enumerator using "to_enum"
static ID id_to_enum = rb_intern ("to_enum");
VALUE method_sym = ID2SYM (rb_intern (meth->primary_name ().c_str ()));
if (argc == 0) {
ret = rba_funcall2_checked (self, id_to_enum, 1, &method_sym);
} else {
std::vector<VALUE> new_args;
new_args.reserve (size_t (argc + 1));
new_args.push_back (method_sym);
new_args.insert (new_args.end (), argv, argv + argc);
ret = rba_funcall2_checked (self, id_to_enum, argc + 1, new_args.begin ().operator-> ());
}
} else { } else {
void *obj = 0; void *obj = 0;

View File

@ -733,6 +733,8 @@ class Basic_TestClass < TestBase
arr = [] arr = []
b.each_b_copy { |bb| arr.push(bb.b2) } b.each_b_copy { |bb| arr.push(bb.b2) }
assert_equal(arr, ["a", "y", "uu"]) assert_equal(arr, ["a", "y", "uu"])
# through enumerator
assert_equal(b.each_b_copy.collect(&:b2), ["a", "y", "uu"])
arr = [] arr = []
b.each_b_copy { |bb| bb.b5(bb.b2 + "x"); arr.push(bb.b2) } b.each_b_copy { |bb| bb.b5(bb.b2 + "x"); arr.push(bb.b2) }
@ -745,6 +747,8 @@ class Basic_TestClass < TestBase
arr = [] arr = []
b.each_b_cref { |bb| arr.push(bb.b2) } b.each_b_cref { |bb| arr.push(bb.b2) }
assert_equal(arr, ["a", "y", "uu"]) assert_equal(arr, ["a", "y", "uu"])
# through enumerator
assert_equal(b.each_b_cref.collect(&:b2), ["a", "y", "uu"])
arr = [] arr = []
# this works, since the "const B &" will be converted to a copy # this works, since the "const B &" will be converted to a copy
@ -759,6 +763,8 @@ class Basic_TestClass < TestBase
arr = [] arr = []
b.each_b_cptr { |bb| arr.push(bb.b2) } b.each_b_cptr { |bb| arr.push(bb.b2) }
assert_equal(arr, ["a", "y", "uu"]) assert_equal(arr, ["a", "y", "uu"])
# through enumerator
assert_equal(b.each_b_cptr.collect(&:b2), ["a", "y", "uu"])
arr = [] arr = []
# const references cannot be modified # const references cannot be modified
@ -778,6 +784,8 @@ class Basic_TestClass < TestBase
arr = [] arr = []
b.each_b_ref { |bb| arr.push(bb.b2) } b.each_b_ref { |bb| arr.push(bb.b2) }
assert_equal(arr, ["a", "y", "uu"]) assert_equal(arr, ["a", "y", "uu"])
# through enumerator
assert_equal(b.each_b_ref.collect(&:b2), ["a", "y", "uu"])
arr = [] arr = []
b.each_b_ref { |bb| bb.b5(bb.b2 + "x"); arr.push(bb.b2) } b.each_b_ref { |bb| bb.b5(bb.b2 + "x"); arr.push(bb.b2) }
@ -790,6 +798,8 @@ class Basic_TestClass < TestBase
arr = [] arr = []
b.each_b_ptr { |bb| arr.push(bb.b2) } b.each_b_ptr { |bb| arr.push(bb.b2) }
assert_equal(arr, ["ax", "yx", "uux"]) assert_equal(arr, ["ax", "yx", "uux"])
# through enumerator
assert_equal(b.each_b_ptr.collect(&:b2), ["ax", "yx", "uux"])
arr = [] arr = []
b.each_b_ptr { |bb| bb.b5(bb.b2 + "x"); arr.push(bb.b2) } b.each_b_ptr { |bb| bb.b5(bb.b2 + "x"); arr.push(bb.b2) }

View File

@ -67,6 +67,8 @@ class DBPolygon_TestClass < TestBase
arr = [] arr = []
a.each_point_hull { |p| arr.push( p.to_s ) } a.each_point_hull { |p| arr.push( p.to_s ) }
assert_equal( arr, ["5,-10", "5,15", "20,15", "20,-10"] ) assert_equal( arr, ["5,-10", "5,15", "20,15", "20,-10"] )
# with enumerator
assert_equal( a.each_point_hull.collect(&:to_s), ["5,-10", "5,15", "20,15", "20,-10"] )
b = a.dup b = a.dup
@ -130,6 +132,8 @@ class DBPolygon_TestClass < TestBase
arr = [] arr = []
a.each_point_hole(0) { |p| arr.push( p.to_s ) } a.each_point_hole(0) { |p| arr.push( p.to_s ) }
assert_equal( arr, ["1,2", "2,2", "2,6"] ) assert_equal( arr, ["1,2", "2,2", "2,6"] )
# with enumerator
assert_equal( a.each_point_hole(0).collect(&:to_s), ["1,2", "2,2", "2,6"] )
arr = [] arr = []
a.each_edge { |p| arr.push( p.to_s ) } a.each_edge { |p| arr.push( p.to_s ) }