From: Paul Brannan Date: 2004-05-18T00:20:03+09:00 Subject: Re: ruby C++ extensions On Fri, May 14, 2004 at 04:43:54PM +0900, Jeff Mitchell wrote: > Second, ruby's exception handling via setjmp/longjmp effectively means > you should never construct a C++ object with a nontrivial destructor > on the stack. If ruby longjmps out of your code, your destructors > will not be called. It's worse than that; if ruby longjmps over the destruction of an automatic object, the program has undefined behavior. And if a C++ exception ever leaves C++ code and goes into Ruby code, the result will also be undefined. C99 programmers also have to be careful; longjmping over the destruction of a variable-length array can result in a memory leak. > Therefore if a ruby method you are implementing requires the use of > temporary C++ objects, you must construct those objects on the heap. If you construct the object on the heap, it's useful to register the object with the Ruby GC so it will get destroyed and not leaked. An alternative is to translate the exception to a C++ exception and then back into a ruby exception. I support both techniques in excruby (http://excruby.sourceforge.net): // Register with the garbage collector class Foo { }; Foo * f = new Foo; REGISTER_WITH_RUBY_GC(Foo, f); // Allocate on the stack RUBY_TRY { Foo f; // rb_cpp_funcall translates Ruby exceptions into the C++ exception // Ruby_Exception; RUBY_CATCH will translate it back for me: rb_cpp_funcall(xyz, rb_intern("foo"), Qtrue); } RUBY_CATCH I've also been playing with an idea somewhat borrowed from boost::python (I haven't released any of this code yet): rb_cFoo = define_class("Foo") .define_creation_funcs( default_allocation_func, &Foo::initialize) .define_method("foo", &Foo::foo); The above code works as is, but eventually I'll also be able to write: class My_Exception_Handler : public Exception_Handler { Std_Exception_Handler(Exception_Handler const * next_exception_handler) : Exception_Handler(next_exception_handler) { } virtual void handle_exception() const { try { call_next_exception_handler(); } catch(My_Exception const & ex) { throw Ruby_Exception(rb_cMy_Exception, ex.what()); } }; // My_Exception_Handler will get used for any exceptions thrown inside // functions in class Foo: rb_cFoo = define_class("Foo") .add_exception_handler() .define_creation_funcs( default_allocation_func, &Foo::initialize) .define_method("foo", &Foo::foo); Also note that SWIG (http://www.swig.org) has support for exceptions, and makes translating C++ exceptions into Ruby exceptions easy (or at least easier than they otherwise would be). Swig also allocates all its objects on the heap, so you don't have to worry about Ruby exceptions unless you explictly call back into Ruby code from your C++ code: %exception { try { $action } catch(std::exception const & ex) { rb_raise(rb_eCPlusPlusException, ex.what()); } } And note too that excruby is not designed to replace SWIG; it's designed so you can use it right alongside SWIG: %exception { RUBY_TRY { $action } RUBY_CATCH } Paul