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 $@ -Iruby -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 methodnew’ 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/

Hi,

···

At Sun, 18 Jan 2004 16:20:01 +0900, Asfand Yar Qazi wrote:

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…

Yes, of course, incompatible. They aren’t concerned with at
all. You have to protect or wrap each exceptions at every
boundaries.


Nobu Nakada

Hi,

···

At Sun, 18 Jan 2004 16:20:01 +0900, Asfand Yar Qazi wrote:

if(rb_scan_args(argc, argv, “12”, &argcppclass, &argmessage) == 1)

This must be “11”, numbers of mandatory arguments and optional
arguments, second is not the total.


Nobu Nakada

As you can see from the code…

#define CHECK_CPP(code) try {code;}
catch(std::exception& e) {raise_ruby(e);}

i.e. catch a C++ exception, wrap its contents in a Ruby exception,
dispatch that Ruby exception. But that corrupts the program stack I
think, resulting in really funny and annoying errors (as documented in
the OP), and eventually resulting in a coredump. Its all due to the
setjmp/longjmp used by Ruby exceptions and GCC’s advanced
exception-handling mechanism, which are incompatible.

I wait for the day when a scripting language will be written in C++, and
be fully compatible will C++ features (perhaps even with templates.)
But still, I’ll just redo my C++ code in a Ruby-esque way, i.e. throwing
Ruby exceptions instead of C++ exceptions, so no problems.

···

nobu.nokada@softhome.net wrote:

Hi,

At Sun, 18 Jan 2004 16:20:01 +0900, > Asfand Yar Qazi wrote:

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…

Yes, of course, incompatible. They aren’t concerned with at
all. You have to protect or wrap each exceptions at every
boundaries.

Ah… one day, someone should do a rewrite on README.EXT :slight_smile:

That said, I used another method and it didn’t do anything differently,
so this can’t have been the problem.

···

nobu.nokada@softhome.net wrote:

Hi,

At Sun, 18 Jan 2004 16:20:01 +0900, > Asfand Yar Qazi wrote:

if(rb_scan_args(argc, argv, “12”, &argcppclass, &argmessage) == 1)

This must be “11”, numbers of mandatory arguments and optional
arguments, second is not the total.