From: Asfand Yar Qazi Date: 2004-01-18T16:20:01+09:00 Subject: C++ from Ruby ARRRGGHH!! Yes, I know there's a wrapper available already that can wrap C++ exceptions in Ruby, but I want to do it myself as a learning exercise. Anyway, could you look at the following test code that reveals the problem? I just don't know what to do! Are C++ exceptions in GCC incompatible with Ruby exceptions? I think that's the problem, isn't it... (I have put a link to an archive with these files, plus the library binary, here: http://users.wmin.ac.uk/~w9921783/ruby-test.tar.gz) =====file Makefile Test.so: ruby-test.cc g++ -shared -o $@ -I`ruby -rrbconfig \ -e 'puts Config::CONFIG["archdir"]'` $< =====file t.rb #!/usr/local/bin/ruby -w require 'Test' include Mod Test.init (1..20).each() do begin Test.init rescue CPP_Error => e puts("C++ exception '#{e.cppclass}' occured, message = \"#{e.message}\""); rescue Exception => e puts("Exception #{e.class.name} occured: #{e.message}"); end end Test.deinit =====file ruby-test.cc #include "ruby.h" #include #include #include namespace { struct Test { static bool init_; static void init() { if(!init_) init_ = true; else throw std::runtime_error("DDStuff"); } static bool is_init() {return init_;} static void deinit() {init_ = false;} }; bool Test::init_ = false; VALUE cTest; /// Module VALUE mMod; /// C++ exception VALUE eCPP_Error; /// Define a class (as rb_define_class) under mMod inline VALUE defclass(const char *name, VALUE super) { return rb_define_class_under(mMod, name, super); } /// Convert boolean to Qtrue/Qfalse inline VALUE convbool(bool arg) {if(arg)return Qtrue; else return Qfalse;} /// Takes an exception 'e', gets its name and 'what' message, and uses /// it to throw a Ruby exception void raise_ruby(std::exception& e) { std::string classname = typeid(e).name(); /* COMMENT: This gives the error message show in Init_Test */ // VALUE excep = rb_funcall(eCPP_Error, rb_intern("new"), 1, // rb_str_new2(classname.c_str())); // rb_raise(excep, e.what()); // COMMENT: WITH THE FOLLOWING instead of the above, when Test.init // has been called a certain number of times, a segfault occurs std::string eval_string = "raise CPP_Error.new('" + classname + "'), '" + e.what() + "'"; rb_eval_string(eval_string.c_str()); } /// @define CHECK_CPP Call given C++ code, and check it for /// exceptions, throwing if necessary #define CHECK_CPP(code) try {code;} catch(std::exception& e) {raise_ruby(e);} // Test VALUE Test__init(VALUE self) { CHECK_CPP(Test::init()); return self; } VALUE Test__is_init(VALUE self) { return convbool(Test::is_init()); } VALUE Test__deinit(VALUE self) { Test::deinit(); return self; } // Call with 'cppclass' VALUE CPP_Error__initialize(int argc, VALUE* argv, VALUE self) { VALUE argcppclass; VALUE argmessage; if(rb_scan_args(argc, argv, "12", &argcppclass, &argmessage) == 1) argmessage = rb_str_new2(""); StringValue(argcppclass); rb_call_super(1, &argmessage); rb_iv_set(self, "@cppclass", argcppclass); return self; } VALUE test(...) { // VALUE retval = INT2FIX(0); // StringValue(retval); // return retval; VALUE tmp = rb_funcall(eCPP_Error, rb_intern("new"), 1, rb_str_new2("AnException")); return tmp; // rb_funcall(rb_mKernel, rb_intern("p"), 1, // rb_funcall(eCPP_Error, rb_intern("inspect"), 0)); // return rb_funcall(eCPP_Error, rb_intern("inspect"), 0); // return eCPP_Error; } } // namespace typedef VALUE(*rbfunk)(...); extern "C" void Init_Test() { // module Mod mMod = rb_define_module("Mod"); rb_define_global_function("testsr", test, 0); /* COMMENT: Using raise-ruby.c block 1, regardless of which of the following styles I try, I get the following: ../t.rb:10:in `init': undefined method `new' for Mod::CPP_Error:Mod::CPP_Error (NoMethodError) from ./t.rb:10 from ./t.rb:8:in `each' from ./t.rb:8 However, see the 'raise_ruby' func above... */ // STYLE1 eCPP_Error = defclass("CPP_Error", rb_eRuntimeError); rb_define_method(eCPP_Error, "initialize", (rbfunk)CPP_Error__initialize, -1); rb_eval_string("class Mod::CPP_Error; attr_reader(:cppclass);end"); // END STYLE1 // STYLE2 // rb_eval_string("module Mod\n" // " class CPP_Error < RuntimeError\n" // " def initialize(cppclass, message = '')\n" // " super(message)\n" // " @cppclass = cppclass\n" // " end\n" // " attr_reader(:cppclass)\n" // " end\n" // "end\n"); // eCPP_Error = rb_eval_string("Mod::CPP_Error"); // STYLE2 cTest = defclass("Test", rb_cObject); rb_define_singleton_method(cTest, "init", (rbfunk)Test__init, 0); rb_define_singleton_method(cTest, "is_init?", (rbfunk)Test__is_init, 0); rb_define_singleton_method(cTest, "deinit", (rbfunk)Test__deinit, 0); } -- http://www.it-is-truth.org/