From: Issac Trotts Date: 2001-12-14T06:27:08+09:00 Subject: [ruby-talk:28455] Re: ANNOUNCE: Extending Ruby With C++ Mini How-To > 1) AFAIK, the only functions you should need extern "C" in > front of are > the Init_xyz functions. If you need to give more functions C linkage, > you might want to mention that it's possible to use extern > "C" in block > form: > > #ifdef __cplusplus > extern "C" { > #endif > void foo(); > void bar(); > #ifdef __cplusplus > } > #endif Good point. > > 2) Generally speaking, ruby methods should have file scope. So either > put the word "static" in front of t_init, t_add, and t_foo, > or surround > them with: > > namespace { //anon > VALUE t_init(VALUE self) { > ... > } > ... > } // namespace anon The GNU C++ compiler didn't like 'extern "C" static VALUE foo() { ... }', warning that it's a mix of storage classes or something like that. The namespace idea should be fine though. > > the latter is more correct, as using static to indicate file > scope is a > deprecated feature of C++. > > The same rule applies to globals; VALUE cTest should probably > have file > scope as well. > > 3) Your t_foo function is not exception-safe. You need to catch C++ > exceptions and re-throw them as Ruby exceptions: > > try { > try { > // do stuff that might throw an exception > } catch(Exception1 & ex) { > VALUE s = rb_str_new2("Exception 1 caught: "); > rb_str_cat2(s, ex.reason_); > throw rb_exc_new3(rb_eRuntimeError, s); > } catch(std::bad_alloc & ex) { > // this probably won't work :( > VALUE s = rb_str_new2("std::bad_alloc"); > throw rb_exc_new3(rb_eNoMemoryError, s); > } > } catch(VALUE ex) { > rb_exc_raise(ex); > return; // get rid of the compiler warning > } catch(...) { > rb_raise(rb_eRuntimeError, "Unknown exception"); > return; // get rid of the compiler warning > } Sure. Maybe I should include something about this later in the HOW-TO. What sorts of exceptions could be thrown by the code I wrote? Maybe the 10-element int array wouldn't get allocated it seems like that will almost never happen. > > I know of no good way to do this without using nested try blocks; note > that when the C++ exception is caught, we re-throw it as a VALUE; when > this happens; the C++ exception gets cleaned up. We then re-raise the > VALUE as a Ruby exception; since VALUE is essentially a > pointer, it does > not need to be cleaned up. > > 4) You do not have the following bug in any of your examples, > but it is > a potential problem. If your code makes a callback to Ruby code, and > the Ruby code raises an exception, then C++ objects on the stack will > not be properly cleaned up. For example, if I change your t_foo > function: > > struct Foo() { > Foo() { std::cout << "Foo()" << std::endl } > ~Foo() { std::cout << "~Foo()" << std::endl } > }; > > VALUE t_foo(VALUE self) { > Foo f; > VALUE x = NUM2INT(Qnil); // this will raise an exception > return Qnil; > } > > Note that f will get constructed, but it may or may not get properly > destructed (the C++ standard does not specify). There are a few > possible solutions: > > 1) create f on the heap and register it with the Ruby gargage > collector (this is what swig does): > > void delete_foo(Foo * f) { > delete f; > } > > VALUE t_foo(VALUE self) { > Foo * f = new Foo; > Data_Wrap_Struct(Foo, 0, delete_foo, f); > VALUE x = NUM2INT(Qnil); // this will raise an exception > return Qnil; > } > > 2) Rewrite the function so that f is destructed before the exception > gets thrown: > > VALUE t_foo(VALUE self) { > { > Foo f; > // do something here with f > } // f should now get destructed > VALUE x = NUM2INT(Qnil); // this will raise an exception > return Qnil; > } > > See [ruby-talk:17943], [ruby-talk:16705], and > [ruby-talk:16697] for more > discussion of exceptions in C++ extensions. > > 5) You might also want to mention that writing Ruby > extensions in C++ is > non-trivial, and give some encouragement not to get discouraged. > Writing correct code is a very difficult problem, and entire > books have > been written on the subject (see Herb Sutter's _Exceptional > C++_, for a > good one pertaining to exception-safe code). Also mention that SWIG > (http://www.swig.org) is a good tool for eliminating many problems you > might run into (though there is no such thing as a magic hammer :). Good points. Thanks for the input. Issac > > Paul >