Overriding virtual C++ method in Ruby

Hi!

Slowly but surly, I'm getting my foot on the ground with embedding Ruby in a C++ application.

So far I can call methods of C++ -classes from the ruby script (using SWIG) and ruby-stuff from C++ using rb_eval_string_protect.

Now I would like to derive a C++-Class in a Ruby script, override a virtual method of the C++ class, return an instance of this new class back to C++ and then invoke the overridden method. I've no idea, if this is possible at all.

See this example and what happens:

[C++:]

class Foo
{
     public:
         virtual char *bar(void) { return "C++ implementation" };
};

[Ruby:]

class FooBar < MyModul::Foo
     def bar
         return "Ruby implementation"
     end
end

test = Foo.new
$uglyGlobal = FooBar.new

STDERR.puts("Ruby-Foo: ", test.bar)
STDERR.puts("Ruby-FooBar: ", $uglyGlobal.bar)

[Back in C++:]

// ...
// initializing ruby and making SWIG-wrapper available
// ...

rb_load_protect(rb_str_new2("test.rb"), Qfalse, &state);
VALUE obj = rb_eval_string_protect("$uglyGlobal", &state);

Foo fooFromCpp = new Foo();
Foo fooFromRuby = *reinterpret_cast<Foo *>(obj);

cerr << "C++-Foo: " << fooFromCpp.bar() << endl;
cerr << "C++-FooBar: " << fooFromRuby.bar() << endl;

[The output is:]

Ruby-Foo: C++ implementation
Ruby-FooBar: Ruby implementation
C++-Foo: C++ implementation
C++-FooBar: C++ implementation

Obviously C++ does not see, that the FooBar object returned by Ruby has overriden bar(). Am I doing something completely wrong?

I'm not sure how polymorphism is implemented in gcc, but I'm afraid something like this is simply not possible, is it?

bye,

Tobias

Please read up on SWIG's cross-language polymorphism feature (a.k.a.
the "directors" feature), described here:

    SWIG and Ruby

and here:

    SWIG and Python

Hope this helps,

Lyle

···

On Fri, 20 Aug 2004 09:34:32 +0900, Tobias Grimm <listaccount@e-tobi.net> wrote:

Now I would like to derive a C++-Class in a Ruby script, override a virtual
method of the C++ class, return an instance of this new class back to C++ and
then invoke the overridden method. I've no idea, if this is possible at all.

Lyle Johnson wrote:

Please read up on SWIG's cross-language polymorphism feature (a.k.a.
the "directors" feature), described here:

Thanks for the hint, I missed this. But when enabling the directors feature
some errors are raised.

Very simple example:

[foo.cc]
class Foo
{
     public:
         virtual char *test(void)
         {
             return "C++ implementation";
         }
         virtual ~Foo() {};
};

[foo.i]
%module(directors="1") ModFoo

%feature("director") Foo;

%include "foo.cc"

[bar.rb]
class Bar < ModFoo::Foo
     def test
         p "ruby implementation of test called"
         return "Ruby Implementation"
     end
end

$foo2 = Bar.new

[main.cc]
int main()
{
     int state;

     ruby_init();
     ruby_init_loadpath();
     SWIG_init();

     rb_load_protect(rb_str_new2("bar.rb"), Qfalse, &state);
     checkRubyError(state);

     Foo *foo1 = new Foo();
     Foo *foo2;

     VALUE obj = rb_eval_string_protect("$foo2", &state);
     checkRubyError(state);

     rb_p(obj);

     Data_Get_Struct(obj, Foo, foo2);

     cout << "calling C++ implementation : " << foo1->test() << endl;
     cout << "calling Ruby implementation: " << foo2->test() << endl;

     ruby_finalize();
     return 0;
}

[Output - directors feature disabled]
#<Bar:0x402c5db8>
calling C++ implementation : C++ implementation
calling Ruby implementation: C++ implementation

[Output - directors feature enabled]
#<Bar:0x402c5c8c>
calling C++ implementation : C++ implementation
"ruby implementation of test called"
(eval): wrong argument type String (expected Data) (TypeError)
(eval): [BUG] Segmentation fault
ruby 1.8.1 (2004-02-03) [i386-linux]

Where can the problem be? the test() implementation of the Ruby Bar-class gets called, but then something seems to go wrong with returning the string value.

Tobias

Tobias Grimm wrote:

Where can the problem be? the test() implementation of the Ruby Bar-class gets called, but then something seems to go wrong with returning the string value.

I figured out, that the following typemap will solve the problem:

%typemap(directorout) char * {
     $result = STR2CSTR($1);
}

Tobias