diff --git a/src/lay/lay/doc/programming/ruby_binding.xml b/src/lay/lay/doc/programming/ruby_binding.xml index 07cc293e2..b79241c2a 100644 --- a/src/lay/lay/doc/programming/ruby_binding.xml +++ b/src/lay/lay/doc/programming/ruby_binding.xml @@ -499,6 +499,16 @@ public: a = A::new a.each { |i| ... } +

+ 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: +

+ +
+# turns all elements returned by the iterator into strings and sorts them
+sorted = a.each.collect(&:to_s).sort
+

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 diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index acbb5e353..d7e9a1091 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -89,7 +89,8 @@ public: { std::vector bt; 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; } @@ -113,7 +114,8 @@ public: // But the purpose is a relative compare, so efficiency is not sacrificed here // for unnecessary consistency. 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) { d += RARRAY_LEN(backtrace); } @@ -933,16 +935,18 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor) if (p) { + static ID id_set = rb_intern ("set"); + VALUE signal_handler = p->signal_handler (meth); if (rb_block_given_p ()) { VALUE proc = rb_block_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) { - ret = rba_funcall2_checked (signal_handler, rb_intern ("set"), argc, argv); + ret = rba_funcall2_checked (signal_handler, id_set, argc, argv); } else { 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); } + } 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 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 { void *obj = 0; diff --git a/testdata/ruby/basic_testcore.rb b/testdata/ruby/basic_testcore.rb index 5eee23a67..655a64a8c 100644 --- a/testdata/ruby/basic_testcore.rb +++ b/testdata/ruby/basic_testcore.rb @@ -733,6 +733,8 @@ class Basic_TestClass < TestBase arr = [] b.each_b_copy { |bb| arr.push(bb.b2) } assert_equal(arr, ["a", "y", "uu"]) + # through enumerator + assert_equal(b.each_b_copy.collect(&:b2), ["a", "y", "uu"]) arr = [] b.each_b_copy { |bb| bb.b5(bb.b2 + "x"); arr.push(bb.b2) } @@ -745,6 +747,8 @@ class Basic_TestClass < TestBase arr = [] b.each_b_cref { |bb| arr.push(bb.b2) } assert_equal(arr, ["a", "y", "uu"]) + # through enumerator + assert_equal(b.each_b_cref.collect(&:b2), ["a", "y", "uu"]) arr = [] # this works, since the "const B &" will be converted to a copy @@ -759,6 +763,8 @@ class Basic_TestClass < TestBase arr = [] b.each_b_cptr { |bb| arr.push(bb.b2) } assert_equal(arr, ["a", "y", "uu"]) + # through enumerator + assert_equal(b.each_b_cptr.collect(&:b2), ["a", "y", "uu"]) arr = [] # const references cannot be modified @@ -778,6 +784,8 @@ class Basic_TestClass < TestBase arr = [] b.each_b_ref { |bb| arr.push(bb.b2) } assert_equal(arr, ["a", "y", "uu"]) + # through enumerator + assert_equal(b.each_b_ref.collect(&:b2), ["a", "y", "uu"]) arr = [] b.each_b_ref { |bb| bb.b5(bb.b2 + "x"); arr.push(bb.b2) } @@ -790,6 +798,8 @@ class Basic_TestClass < TestBase arr = [] b.each_b_ptr { |bb| arr.push(bb.b2) } assert_equal(arr, ["ax", "yx", "uux"]) + # through enumerator + assert_equal(b.each_b_ptr.collect(&:b2), ["ax", "yx", "uux"]) arr = [] b.each_b_ptr { |bb| bb.b5(bb.b2 + "x"); arr.push(bb.b2) } diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index 0aaec4869..73c592830 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -67,6 +67,8 @@ class DBPolygon_TestClass < TestBase arr = [] a.each_point_hull { |p| arr.push( p.to_s ) } 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 @@ -130,6 +132,8 @@ class DBPolygon_TestClass < TestBase arr = [] a.each_point_hole(0) { |p| arr.push( p.to_s ) } 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 = [] a.each_edge { |p| arr.push( p.to_s ) }