I'm trying to call a class method from C++. I've tried all combinations of rb_intern I could think of to make it work, but I've gotten nothing.
Example class
class CallTest def go (do something here) end end
Trying to call in C++:
rb_funcall(?, rb_intern("go"), 0);
What goes in the ? space? I know if I use Qnil there, it will call global functions, but I'd prefer class methods.
Am I heading in the wrong direction?
Also, I'd prefer not to have to know the class name ahead of time if possible, but if I have to require that I know what it is, I can try passing it by name to my application.
I'm using SWIG to generate the binding.
There are two standard approaches for defining class method in Ruby. The first one is the “def self. method” (let's call it Style #1), and the second one is the “class << self” (let's call it Style #2). Both of them have pros and cons.
A class method can be called without creating a new instance of the class. class Foo def self. method puts "I'm a class method!" end end Foo. method # => "I'm a class method!"
First off, go
is, as you've defined it, not a class method, but an instance method.
As an object oriented language, all ruby methods require a receiver, that is, an object that the method is invoked on. For instance methods, the receiver is an instance of the class, for class methods, the receiver is the class object itself.
The ? placeholder you have is the slot for the receiver of the method call.
If you want to leave it as an instance method, then you need to do this:
rb_funcall(a_CallTest_instance, rb_intern("go"), 0);
where a_CallTest_instance
was an instance of CallTest you created using rb_class_new_instance()
.
If you make it into a class method:
class CallTest
def self.go
# ...
end
end
Then you need to use the CallTest
class itself as the receiver:
rb_funcall(klass, rb_intern("go"), 0);
You can get a reference to the CallTest
class using rb_const_get()
VALUE klass = rb_const_get(rb_cObject, rb_intern('CallTest'));
Use rb_cObject
there, since CallTest
is defined in the global context.
I'd suggest reading the chapter in the Pickaxe about extending ruby.
I also use SWIG. These specific example files should help you.
1) test.rb
require 'test.so'
class A
def func1(buffer)
puts "ruby instance method: #{buffer}"
end
end
def func2(buffer)
puts "ruby global method: #{buffer}"
end
module Z
def Z.func3(buffer)
puts "ruby module method: #{buffer}"
end
end
a = A.new
t = Test::Test1.new()
t.call(a, "func1", "Hello", 5)
t.call(method(:func2), "func2", "Yabaaa", 6)
t.call(Z, "func3", "Yahooooooo", 10)
2) test.h:
#include <ruby.h>
class Test1
{
public:
void call(VALUE obj, char* func_name, const char* buffer, int size)
{
VALUE val = rb_str_new(buffer, size);
rb_funcall(obj, rb_intern(func_name), 1, val);
}
};
3) test.i:
%module test
%{
#include "test.h"
%}
%exception
{
try
{
$action
}
catch (std::exception& ex)
{
static VALUE cpperror = rb_define_class("test Exception", rb_eStandardError);
rb_raise(cpperror, ex.what());
}
catch (...)
{
static VALUE cpperror = rb_define_class("test UnknownException", rb_eStandardError);
rb_raise(cpperror, "Unknown catched");
}
}
%include "test.h"
OUTPUT:
ruby ./test.rb
ruby instance method: Hello
ruby global method: Yabaaa
ruby module method: Yahooooooo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With