From: Paul Brannan Date: 2007-12-28T00:27:16+09:00 Subject: Re: C++ Functors and Ruby extensions 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 f1; ... > void Init_function_test() > { > f1 = boost::bind(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.