[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

C++ Functors and Ruby extensions

Jason Roelofs

12/24/2007 7:03:00 PM

[Note: parts of this message were removed to make it a legal post.]

I wonder if anyone has tried to do what I'm doing and if they've come up
with an appropriate solution.

Basic idea:

struct ruby_func_wrapper
{
VALUE operator()(VALUE self, VALUE args)
{
m_callback(args ...);
}
}

extern "C"
void Init_functor_test() {
ruby_func_wrapper functor;
[ ... code that sets callback of functor ... ]

rb_define_global_function("functor_test", [something here &functor], -2);
}

Basically, I want to use instances of the ruby_func_wrapper functor as
callback for Ruby methods. I'm using Boost right now, and have tried
Boost.Function and Boost.Bind to no avail (the code compiles but the actual
call causes a seg fault, I assume the memory location is voided after the
init is done, so trying to pass execution to a bad memory location, but I'm
not sure). For the record, I am trying my hand at a Boost.Ruby library.

I'm also wondering if there is another way to add methods to Kernel or
Classes outside of the rb_define_* methods. In Python you can do
PyObject_SetAttr(namespace, name, PyCFunction) which is effectively ("
namespace.name = function"). I've yet to find an equivalent in Ruby.

I hope this is clear, though if not I can post the full code I've got right
now.

Thanks

Jason

9 Answers

Axel Etzold

12/25/2007 9:28:00 AM

0


-------- Original-Nachricht --------
> Datum: Tue, 25 Dec 2007 04:02:38 +0900
> Von: "Jason Roelofs" <jameskilton@gmail.com>
> An: ruby-talk@ruby-lang.org
> Betreff: C++ Functors and Ruby extensions

> I wonder if anyone has tried to do what I'm doing and if they've come up
> with an appropriate solution.
>
> Basic idea:
>
> struct ruby_func_wrapper
> {
> VALUE operator()(VALUE self, VALUE args)
> {
> m_callback(args ...);
> }
> }
>
> extern "C"
> void Init_functor_test() {
> ruby_func_wrapper functor;
> [ ... code that sets callback of functor ... ]
>
> rb_define_global_function("functor_test", [something here &functor],
> -2);
> }
>
> Basically, I want to use instances of the ruby_func_wrapper functor as
> callback for Ruby methods. I'm using Boost right now, and have tried
> Boost.Function and Boost.Bind to no avail (the code compiles but the
> actual
> call causes a seg fault, I assume the memory location is voided after the
> init is done, so trying to pass execution to a bad memory location, but
> I'm
> not sure). For the record, I am trying my hand at a Boost.Ruby library.
>
> I'm also wondering if there is another way to add methods to Kernel or
> Classes outside of the rb_define_* methods. In Python you can do
> PyObject_SetAttr(namespace, name, PyCFunction) which is effectively ("
> namespace.name = function"). I've yet to find an equivalent in Ruby.
>
> I hope this is clear, though if not I can post the full code I've got
> right
> now.
>
> Thanks
>
> Jason

Dear Jason,

I think SWIG (http://www...) may have a solution
for what you want to do:

http://www.goto.info.waseda.ac.jp/~fukusima/ruby/swig-examples/funcptr/...

Here's some more information on how to use it in conjunction with
Ruby :

http://www...Doc1.3/Ruby.html

Best regards,

Axel

--
GMX FreeMail: 1 GB Postfach, 5 E-Mail-Adressen, 10 Free SMS.
Alle Infos und kostenlose Anmeldung: http://www.gmx.net/de/g...

Jason Roelofs

12/25/2007 5:55:00 PM

0

[Note: parts of this message were removed to make it a legal post.]

I'm doing this because I SWIG doesn't work for my current wrapping project.

Also, SWIG generates static method wrappers. I'm trying to do this
dynamically (see Boost.Python: http://www.boost.org/libs/p...)

Jason

On Dec 25, 2007 4:27 AM, Axel Etzold <AEtzold@gmx.de> wrote:

>
> -------- Original-Nachricht --------
> > Datum: Tue, 25 Dec 2007 04:02:38 +0900
> > Von: "Jason Roelofs" <jameskilton@gmail.com>
> > An: ruby-talk@ruby-lang.org
> > Betreff: C++ Functors and Ruby extensions
>
> > I wonder if anyone has tried to do what I'm doing and if they've come up
> > with an appropriate solution.
> >
> > Basic idea:
> >
> > struct ruby_func_wrapper
> > {
> > VALUE operator()(VALUE self, VALUE args)
> > {
> > m_callback(args ...);
> > }
> > }
> >
> > extern "C"
> > void Init_functor_test() {
> > ruby_func_wrapper functor;
> > [ ... code that sets callback of functor ... ]
> >
> > rb_define_global_function("functor_test", [something here &functor],
> > -2);
> > }
> >
> > Basically, I want to use instances of the ruby_func_wrapper functor as
> > callback for Ruby methods. I'm using Boost right now, and have tried
> > Boost.Function and Boost.Bind to no avail (the code compiles but the
> > actual
> > call causes a seg fault, I assume the memory location is voided after
> the
> > init is done, so trying to pass execution to a bad memory location, but
> > I'm
> > not sure). For the record, I am trying my hand at a Boost.Ruby library.
> >
> > I'm also wondering if there is another way to add methods to Kernel or
> > Classes outside of the rb_define_* methods. In Python you can do
> > PyObject_SetAttr(namespace, name, PyCFunction) which is effectively ("
> > namespace.name = function"). I've yet to find an equivalent in Ruby.
> >
> > I hope this is clear, though if not I can post the full code I've got
> > right
> > now.
> >
> > Thanks
> >
> > Jason
>
> Dear Jason,
>
> I think SWIG (http://www...) may have a solution
> for what you want to do:
>
>
> http://www.goto.info.waseda.ac.jp/~fukusima/ruby/swig-examples/funcptr/index.html<http://www.goto.info.waseda.ac.jp/%7Efukusima/ruby/swig-examples/funcptr/inde...
>
> Here's some more information on how to use it in conjunction with
> Ruby :
>
> http://www...Doc1.3/Ruby.html
>
> Best regards,
>
> Axel
>
> --
> GMX FreeMail: 1 GB Postfach, 5 E-Mail-Adressen, 10 Free SMS.
> Alle Infos und kostenlose Anmeldung: http://www.gmx.net/de/g...
>
>

gga

12/26/2007 2:40:00 AM

0

On Dec 24, 4:02 pm, Jason Roelofs <jameskil...@gmail.com> wrote:
> I'm using Boost right now, and have tried
> Boost.Function and Boost.Bind to no avail (the code compiles but the actual
> call causes a seg fault, I assume the memory location is voided after the
> init is done, so trying to pass execution to a bad memory location, but I'm
> not sure). For the record, I am trying my hand at a Boost.Ruby library.
>

Your assumption is correct. You can't do it. The functor needs to
remain visible in memory. Also, depending on your compiler, the way C+
+ functions expect arguments may be different from what ruby expects
in its C API (this is usually a problem with Windows' __stdcall vs.
__fastcall, etc).
You will also not get ANY speed benefit from using a functor, thou, so
using it is kind of pointless.


> I'm also wondering if there is another way to add methods to Kernel or
> Classes outside of the rb_define_* methods. In Python you can do
> PyObject_SetAttr(namespace, name, PyCFunction) which is effectively ("
> namespace.name = function"). I've yet to find an equivalent in Ruby.

No.

class A
def f; end
end

repeat as many times as needed. Classes are open in ruby.
rb_define_* do just the above and is equivalent to python's adding of
functions.

Overall, as the other poster said, you should use SWIG. There's
really no benefit to using a C++ approach to wrapping code like
Boost.Python does. If anything, Boost.Python is much more primitive
than what swig can do.

Jason Roelofs

12/26/2007 11:08:00 PM

0

[Note: parts of this message were removed to make it a legal post.]

On Dec 25, 2007 9:44 PM, gga <GGarramuno@aol.com> wrote:

> On Dec 24, 4:02 pm, Jason Roelofs <jameskil...@gmail.com> wrote:
> > I'm using Boost right now, and have tried
> > Boost.Function and Boost.Bind to no avail (the code compiles but the
> actual
> > call causes a seg fault, I assume the memory location is voided after
> the
> > init is done, so trying to pass execution to a bad memory location, but
> I'm
> > not sure). For the record, I am trying my hand at a Boost.Ruby library.
> >
>
> Your assumption is correct. You can't do it. The functor needs to
> remain visible in memory. Also, depending on your compiler, the way C+
> + functions expect arguments may be different from what ruby expects
> in its C API (this is usually a problem with Windows' __stdcall vs.
> __fastcall, etc).
> You will also not get ANY speed benefit from using a functor, thou, so
> using it is kind of pointless.
>
>
> > I'm also wondering if there is another way to add methods to Kernel or
> > Classes outside of the rb_define_* methods. In Python you can do
> > PyObject_SetAttr(namespace, name, PyCFunction) which is effectively ("
> > namespace.name = function"). I've yet to find an equivalent in Ruby.
>
> No.
>
> class A
> def f; end
> end
>
> repeat as many times as needed. Classes are open in ruby.
> rb_define_* do just the above and is equivalent to python's adding of
> functions.
>
> Overall, as the other poster said, you should use SWIG. There's
> really no benefit to using a C++ approach to wrapping code like
> Boost.Python does. If anything, Boost.Python is much more primitive
> than what swig can do.
>
>
SWIG does not handle nested classes, a *serious* defect to what I'm trying
to do. I've looked into helping add this feature, but the amount of work
required makes it more feasible to build a better, Ruby-specific wrapper
system.

And comparing Boost.Python to SWIG really doesn't make sense. You need to
compare Boost.Python with Py++ to SWIG.

I'm going to post this to Ruby-core as well to see if I can glean any
insight from those who know the innards of Ruby.

For Ruby-core: Is the following code bit even possible or feasible, to the
best of your knowledge. Basically, I need to send a dynamically generated
function pointer to rb_define_* methods. If this is not possible, I guess I
could go with method_missing, and dispatch the call in C++ according to the
name, but man, that sounds hacky.

Anyway, here is the proto code I'm using to figure this out:

function_test.cpp:

#include "ruby.h"

#include <iostream>

#include <boost/function.hpp>
#include <boost/bind.hpp>

using namespace std;

typedef VALUE (ruby_method)(...);

struct ruby_func
{
VALUE operator()(VALUE self, VALUE args) {
cout << "In functor" << endl;
//cout << "Function called, name " << m_name << endl;
return Qnil;
}
};

ruby_func func;
boost::function2<VALUE, VALUE, VALUE> f1;

VALUE func_check(VALUE self, VALUE args) {
if (f1) {
cout << "Function object exists" << endl;
// And just to prove, call it
f1(Qnil, Qnil);
} else {
cout << "Function object does not exist" << endl;
}
return Qnil;
}

extern "C"
void Init_function_test()
{
f1 = boost::bind<VALUE>(func, _1, _2);

// Prove that boost::bind worked correctly
f1(Qnil, Qnil);

// Method created to check that the function object still exists in memory
rb_define_global_function("check_function", (ruby_method*) &func_check,
-2);

// Actual function call, this causes segfault
rb_define_global_function("do_function_test", (ruby_method*) &f1, -2);
}


test.rb:

require 'function_test'

# Proper output from extension
check_function

# Segfault
do_function_test




Thanks for your help.

Jason

Robert Klemme

12/27/2007 12:37:00 PM

0

2007/12/27, Jason Roelofs <jameskilton@gmail.com>:
> SWIG does not handle nested classes, a *serious* defect to what I'm trying
> to do. I've looked into helping add this feature, but the amount of work
> required makes it more feasible to build a better, Ruby-specific wrapper
> system.

I'm not too familiar with Boost. So could you quickly summarize what
is it that you expect to gain from creating a Boost Ruby integration?

Kind regards

robert


--
use.inject do |as, often| as.you_can - without end

Jason Roelofs

12/27/2007 2:46:00 PM

0

[Note: parts of this message were removed to make it a legal post.]

On Dec 27, 2007 7:36 AM, Robert Klemme <shortcutter@googlemail.com> wrote:

> 2007/12/27, Jason Roelofs <jameskilton@gmail.com>:
> > SWIG does not handle nested classes, a *serious* defect to what I'm
> trying
> > to do. I've looked into helping add this feature, but the amount of work
> > required makes it more feasible to build a better, Ruby-specific wrapper
> > system.
>
> I'm not too familiar with Boost. So could you quickly summarize what
> is it that you expect to gain from creating a Boost Ruby integration?
>
> Kind regards
>
> robert
>
>
> --
> use.inject do |as, often| as.you_can - without end
>
>
Boost.Python and luabind are libraries built to make it extremely easy to
build interfaces into the target language from C++. It's not really about
integration with Boost, Boost just provides some very, very useful
constructs and a powerful meta-programming subsystem that makes this library
feasible in such a strict language. Here's what you're able to do:

Wrap this:

class A {
public:
A();
A(int, int);

void doSomething();
int getSomethingBack();
};

Like this:

class_<A>("A")
.def(initialize<int, int>())
.def("do_something", &A::doSomething)
.def("get_something_back", &A::getSomethingBack);

and in Ruby

a1 = A.new
a = A.new(1,2)

a.do_something
a.get_something_back


For those of you pushing SWIG, trust me, I've spent many, many hours trying
different ways to make nested classes work in a way that won't require me to
re-write a full quarter of the headers that I'm trying to wrap (Ogre
rendering engine, for the record). Nested classes are *not* supported in
SWIG, there are hacks to make it look so, hacks that do not work all of the
time.

Jason

Paul Brannan

12/27/2007 3:27:00 PM

0

On Thu, Dec 27, 2007 at 08:07:31AM +0900, Jason Roelofs wrote:
> For Ruby-core: Is the following code bit even possible or feasible, to
> the best of your knowledge. Basically, I need to send a dynamically
> generated function pointer to rb_define_* methods. If this is not

Your code doesn't use dynamically-generated function pointers; it uses
function objects. If you want dynamically-generated function pointers
(which you probably don't), you'll need to use something like ffcall or
similar.

Ruby can't call function objects directly; it can only call function
pointers. Unfortunately, the Ruby API doesn't provide a mechanism to
attach data to a particular method, which is necessary in order to
provide call a function object from inside a function.

The Rice library uses function objects to automatically do type
conversion from Ruby type to C++ type (and back again when the function
returns). It gets around the above limitation by mapping class name and
method name to the data, then doing a lookup (*) when the function is
called to get a pointer to a base class. The conversion function is
then just a virtual function call on the retrieved pointer.

Take a look at rice/detail/method_data.cpp if you want the gory details.

> possible, I guess I could go with method_missing, and dispatch the
> call in C++ according to the name, but man, that sounds hacky.

Using method_missing would be hacky. Fortunately, on Ruby 1.8, you can
get the name of the function just called via ruby_frame->last_class and
ruby_frame->last_func. Ruby 1.9 gives an interface to get the method
name via rb_frame_callee() (**).

> typedef VALUE (ruby_method)(...);
...
> boost::function2<VALUE, VALUE, VALUE> f1;
...
> void Init_function_test()
> {
> f1 = boost::bind<VALUE>(func, _1, _2);
> // Prove that boost::bind worked correctly
> f1(Qnil, Qnil);
> // Method created to check that the function object still exists in
> memory
> rb_define_global_function("check_function", (ruby_method*)
> &func_check, -2);
> // Actual function call, this causes segfault
> rb_define_global_function("do_function_test", (ruby_method*) &f1,
> -2);

This cast is invalid. f1 is a boost::function, not a function pointer.
You need to create a wrapper function as mentioned above.

Incidentally, at one point I attempted to add support for function
objects to Rice, but I gave up when I realized that I couldn't make it
work with boost::bind (since operator() in the object returned by
boost::bind is a template function, which makes introspection on the
parameter types and return type impossible). I might give this another
shot sometime, but it's unfortunately non-trivial.

> }

Paul


(*) To avoid memory allocation, the actual data is stored in the unused
member of the CFUNC node that Ruby allocates when the method is defined.

(**) AFAICT, there's no function interface to get the class that was
just called. I'll likely cross this bridge when I port Rice to 1.9.


Paul Brannan

12/27/2007 3:33:00 PM

0

On Thu, Dec 27, 2007 at 11:46:24PM +0900, Jason Roelofs wrote:
> Wrap this:
>
> class A {
> public:
> A();
> A(int, int);
>
> void doSomething();
> int getSomethingBack();
> };
>
> Like this:
>
> class_<A>("A")
> .def(initialize<int, int>())
> .def("do_something", &A::doSomething)
> .def("get_something_back", &A::getSomethingBack);

Rice (http://rubyforge.org/pro...) lets you do it like this:

extern "C"
void Init_myextension()
{
define_class<A>("A")
.define_constructor<Constructor<A>())
.define_method("do_something, &A::doSomething)
.define_method("get_something_back", &A::getSomethingBack);
}

I avoided the boost::python syntax so the library would feel more
familiar to people who use the C Ruby API.

I don't yet have support for non-default constructors working, but I'd
welcome the help. :)

Paul


Robert Klemme

12/27/2007 10:19:00 PM

0

On 27.12.2007 15:46, Jason Roelofs wrote:
> [Note: parts of this message were removed to make it a legal post.]
>
> On Dec 27, 2007 7:36 AM, Robert Klemme <shortcutter@googlemail.com> wrote:
>
>> 2007/12/27, Jason Roelofs <jameskilton@gmail.com>:
>>> SWIG does not handle nested classes, a *serious* defect to what I'm
>> trying
>>> to do. I've looked into helping add this feature, but the amount of work
>>> required makes it more feasible to build a better, Ruby-specific wrapper
>>> system.
>> I'm not too familiar with Boost. So could you quickly summarize what
>> is it that you expect to gain from creating a Boost Ruby integration?
>>
>> Kind regards
>>
>> robert
>>
>>
>> --
>> use.inject do |as, often| as.you_can - without end
>>
>>
> Boost.Python and luabind are libraries built to make it extremely easy to
> build interfaces into the target language from C++. It's not really about
> integration with Boost, Boost just provides some very, very useful
> constructs and a powerful meta-programming subsystem that makes this library
> feasible in such a strict language. Here's what you're able to do:
>
> Wrap this:
>
> class A {
> public:
> A();
> A(int, int);
>
> void doSomething();
> int getSomethingBack();
> };
>
> Like this:
>
> class_<A>("A")
> .def(initialize<int, int>())
> .def("do_something", &A::doSomething)
> .def("get_something_back", &A::getSomethingBack);
>
> and in Ruby
>
> a1 = A.new
> a = A.new(1,2)
>
> a.do_something
> a.get_something_back

Thanks for taking the time to explain! I was not aware that something
like this is part of Boost.

> For those of you pushing SWIG, trust me, I've spent many, many hours trying
> different ways to make nested classes work in a way that won't require me to
> re-write a full quarter of the headers that I'm trying to wrap (Ogre
> rendering engine, for the record). Nested classes are *not* supported in
> SWIG, there are hacks to make it look so, hacks that do not work all of the
> time.

Kind regards

robert