On 4/7/07, Noah Easterly <noah.easterly / gmail.com> wrote: > On Apr 7, 1:08 pm, "Kent Sibilev" <ksr... / gmail.com> wrote: > > You should wrap your rb_yield call with rb_ensure. From README.EXT: > > > > VALUE rb_ensure(VALUE (*func1)(), void *arg1, void (*func2)(), void *arg2) > > > > Calls the function func1 with arg1 as the argument, then calls func2 > > with arg2 if execution terminated. The return value from > > rb_ensure() is that of func1. > > Hmm.... this doesn't seem to have the desired effect. I'm pretty sure > rb_ensure() is just for handling exceptions, not breaks. > > According to ruby.h and the Pickaxe, the function def'n has changed a > bit too. From Programming Ruby(2nd ed): > > VALUE rb_ensure(VALUE(*body)(), VALUE args, VALUE(*ensure)(), VALUE > eargs) > > Executes body with the given args. Whether or not an exception is > raised, execute ensure with the given eargs after body has completed. > > So it seems that rb_ensure is just for exceptions, not 'break'. I > tried tweaking my code to use it anyway, but the ensure call is still > bypassed when there is a break. For example: > > >>> lib.c > void func( int n, int (*callback)(void) ) > { > printf("before callback...\n"); > if (callback() < 0) > goto cleanup; > printf("after callback...\n"); > cleanup: > printf("in cleanup...\n"); > } > >>> ext.c > static void > my_ensure(void * ptr) > { > *(int *)ptr = -1; > } > static int > meth_callback(void) > { > int retval = 0; > rb_ensure(rb_yield, Qnil, my_ensure, &retval); > return retval; > } > // ... > >>> test.rb > > require 'ext' > YYY.new.meth(100) { } # outputs "before callback...\n", "in cleanup... > \n" > YYY.new.meth(100) { break } # outputs "before callback...\n" > > Which makes sense if, when there is no break, the my_ensure func gets > called (as it should), so processing skips to cleanup, and when there > is a break, the ensure func does not get called, and processing skips > back to the ruby script. > > I suppose I could switch to having people 'raise BreakException', > rather than 'break' within the block, but that seems like its hobbling > the ruby language. > > Thanks for the idea, though! It sounds like you want to do the equivalent of turning the given block into a lambda. The break is causing a return from the block. Here's some ruby code which shows the problem and the solution: rick@frodo:/public/rubyscripts$ cat lambda.rb def with_yield puts "before yield" yield puts "got back" end def with_call(&block) puts "before call" block.call puts "got back" end def with_lambda(&block) puts "before lambda call" (lambda &block).call puts "got back" end with_yield {puts "in block"; break} puts with_call {puts "in block"; break} puts with_lambda {puts "in block"; break} rick@frodo:/public/rubyscripts$ ruby lambda.rb before yield in block before call in block before lambda call in block got back Now just turn that into C code. You need rb_scan_args with a format of "&" on the last argument to get the block. rb_funcall2 to call the lambda method which is private And you probably also want to use rb_ensure in case the block raises and exception as well. -- Rick DeNatale My blog on Ruby http://talklikeaduck.denhaven2.com/