On 4/7/07, Kent Sibilev <ksruby / gmail.com> wrote:
> 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"
> >
>
> What I meant was that your cleanup code should be in my_ensure
> function and not in func itself. Just to prove my point:
>
> $ cat t.rb
> def test
>   puts 'pre'
>   yield
>   puts 'post'
> ensure
>   puts 'ensure'
> end
>
> test {break}
>
> $ ruby t.rb
> pre
> ensure
>
>

OK, maybe this code will clean things up:

$ cat t.rb
require 'rubygems'
require 'inline'

class MyTest
  inline do |builder|
    builder.prefix <<-EOC
      static VALUE
      func(VALUE arg)
      {
        printf("pre\\n");
        rb_yield(arg);
        printf("post\\n");
        return Qnil;
      }
      static VALUE
      cleanup(VALUE arg)
      {
        printf("ensure\\n");
        return Qnil;
      }
    EOC
    builder.c <<-EOC
      void my_func()
      {
        rb_ensure(func, Qnil, cleanup, Qnil);
      }
    EOC
  end
end

MyTest.new.my_func {break}

$ ruby t.rb
pre
ensure
$


-- 
Kent
---
http://www.datanoise.com