Not logged inGosu Forums
Forum back to libgosu.org Help Search Register Login
Up Topic Gosu / Gosu Exchange / bug in translate et al., or just a shortcoming of Ruby?
- - By erisdiscord Date 2010-08-23 03:46
Wanna see something messed up?

translate (@scroll_x, @scroll_y) { return }

Oops, now everything else gets translated too. Here's how to fix it.

rb_ensure(rb_yield, Qnil, hypotheticalPopTransformWrapperThatAcceptsAnInstanceOfGraphics, (VALUE)&($self->graphics() ) );

How ugly is that? :D Forgive the parentheses, but I'm never sure whether & binds more tightly than -> or not!
Parent - - By banister Date 2010-08-23 04:14
im guessing those blocks aren't exception safe either (though not such a big deal of course)...the: begin; block.call; ensure; clean_up_code; end, is pretty standard ruby when messing with blocks. Much easier to do in Ruby than in the C api though. iirc, in the C API it's got a horrible interface all to do with passing functions pointers around
Parent - - By erisdiscord Date 2010-08-23 04:32
Yeah, that's what the rb_ensure(…) nonsense is that I posted below the problem code. I guess I forgot to change the topic after I peeked at Gosu's source. :D

For the curious, this is its signature in Ruby 1.9.2:

VALUE rb_ensure(VALUE(*)(ANYARGS),VALUE,VALUE(*)(ANYARGS),VALUE);

For the masochistic, here's its source code:

VALUE
rb_ensure(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE (*e_proc)(ANYARGS), VALUE data2)
{
    int state;
    volatile VALUE result = Qnil;

    PUSH_TAG();
    if ((state = EXEC_TAG()) == 0) {
  result = (*b_proc) (data1);
    }
    POP_TAG();
    /* TODO: fix me */
    /* retval = prot_tag ? prot_tag->retval : Qnil; */     /* save retval */
    (*e_proc) (data2);
    if (state)
  JUMP_TAG(state);
    return result;
}


That "fix me" comment is a little unnerving. Anyway, from this we can at least figure out which parameters do what. If Ruby's ensure block were somehow implemented using Ruby's ensure block, it might look something like this:

def rb_ensure b_proc, data1, e_proc, data2
  result = nil
  begin
    result = b_proc.call data1
  ensure
    e_proc.call data2
  end
  return result
end
Parent - - By kyonides Date 2010-08-23 05:28
To me it looks more like a rewrite of ensure, which is already included in Ruby... but I might be wrong...
Parent - By erisdiscord Date 2010-08-23 05:32
The tangled mess of C code? It is the implementation of ensure, straight from Ruby's source. The Ruby code at the bottom? It's a clarification of what the C code does. :)
Parent - - By banister Date 2010-08-23 07:19
yeah it's a mess.

When i need to use the begin;ensure;end functionality in a C extension, i don't. Instead i expose the action code and cleanup code as hidden methods and then invoke them from an accompanying ruby script, like so (as i did in object2module):

      begin
          mod = o.__to_module__
          include(mod)
      ensure
          mod.__reset_tbls__ if mod != o && o != Object &&
                                                   o != Class && o != Module
      end
Parent - By erisdiscord Date 2010-08-23 07:24
Ick, that's probably for the best. I wonder if there's a cleaner way to implement it with C. It could probably be made less ugly with Apple's C blocks if they make it into the standard… Well, a little less ugly anyway.

I've discovered that some aspects of Ruby's C API are really nice, but there are some things that just make me cringe. Plus it's kind of cluttered with a lot of redundant macros and stuff that aren't even namespaced.
Parent - - By jlnr (dev) Date 2010-08-23 08:26 Edited 2010-08-23 08:29
Huh. I guess it's not that bad because even if an exception happens during rendering, the frame shouldn't be flipped (?), unless you catch the error within the rendering, but even then only one frame gets trashed in good cases.

The bigger question to me is... why does return trigger this too?? Ruby keeps puzzling me after years o_O



I actually think I did something with rb_ensure myself before I used SWIG. It wasn't really that bad, I could probably just use macros to generate the hypothetical wrapper functions. Is there a good use case for this so that I should move it up on my priority list? :)
Parent - - By erisdiscord Date 2010-08-23 08:47
The bigger question is actually pretty easy to answer, but difficult for me to describe. Better to show you with an example from my own code.

# Recursively tests whether +point+ falls within one of the container's
# subviews. Returns the first subview that tested positively, +self+ if
# no subviews contain the point, or +nil+ if the container does not
# contain the point.
def point_query point
  if frame.include? point
    # query each subview and return the first non-nil result
    point -= frame.offset
    @subviews.each do |subview|
      found_view = subview.point_query point
      return found_view if found_view
    end
   
    # fall back on self if no subview contains the point
    self
  else
    nil
  end
end


If blocks didn't behave this way, then that return statement would basically be a no op. The block would return, the enumerator would move on to the next item in @subviews and the method would eventually return self. If I wanted to return the result of the subview's point_query, I'd have to write something significantly nastier using a catch/throw or something. I'm sure if it worked that way I'd figure something out.

Let me know if you're not convinced this is the right way to do it. :)
Parent - By jlnr (dev) Date 2010-08-23 08:58
OK, got it. :) Shouldn't have been surprising to me that ensure has to handle return too. I guess I am already thinking too much in stack destructors, or rather, they are thinking too much for me. ;)
Up Topic Gosu / Gosu Exchange / bug in translate et al., or just a shortcoming of Ruby?

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill