从C ++调用Ruby类方法

时间:2022-04-15 22:27:53

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.

我正在尝试从C ++调用类方法。我已经尝试过rb_intern的所有组合,我可以想到让它工作,但我什么都没有。

Example class

class CallTest
   def go
    (do something here)
   end
end

Trying to call in C++:

试图用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.

怎么了?空间?我知道如果我在那里使用Qnil,它会调用全局函数,但我更喜欢类方法。

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.

我正在使用SWIG生成绑定。

2 个解决方案

#1


First off, go is, as you've defined it, not a class method, but an instance method.

首先,go,就像你定义的那样,不是类方法,而是实例方法。

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.

作为面向对象的语言,所有ruby方法都需要一个接收器,即调用该方法的对象。例如方法,接收器是类的实例,对于类方法,接收器是类对象本身。

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().

其中a_CallTest_instance是您使用rb_class_new_instance()创建的CallTest的实例。

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:

然后你需要使用CallTest类本身作为接收器:

rb_funcall(klass, rb_intern("go"), 0);

You can get a reference to the CallTest class using rb_const_get()

您可以使用rb_const_get()获取对CallTest类的引用

VALUE klass = rb_const_get(rb_cObject, rb_intern('CallTest'));

Use rb_cObject there, since CallTest is defined in the global context.

在那里使用rb_cObject,因为CallTest是在全局上下文中定义的。

I'd suggest reading the chapter in the Pickaxe about extending ruby.

我建议阅读Pickaxe中有关扩展红宝石的章节。

#2


I also use SWIG. These specific example files should help you.

我也使用SWIG。这些特定的示例文件可以帮助您。

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

#1


First off, go is, as you've defined it, not a class method, but an instance method.

首先,go,就像你定义的那样,不是类方法,而是实例方法。

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.

作为面向对象的语言,所有ruby方法都需要一个接收器,即调用该方法的对象。例如方法,接收器是类的实例,对于类方法,接收器是类对象本身。

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().

其中a_CallTest_instance是您使用rb_class_new_instance()创建的CallTest的实例。

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:

然后你需要使用CallTest类本身作为接收器:

rb_funcall(klass, rb_intern("go"), 0);

You can get a reference to the CallTest class using rb_const_get()

您可以使用rb_const_get()获取对CallTest类的引用

VALUE klass = rb_const_get(rb_cObject, rb_intern('CallTest'));

Use rb_cObject there, since CallTest is defined in the global context.

在那里使用rb_cObject,因为CallTest是在全局上下文中定义的。

I'd suggest reading the chapter in the Pickaxe about extending ruby.

我建议阅读Pickaxe中有关扩展红宝石的章节。

#2


I also use SWIG. These specific example files should help you.

我也使用SWIG。这些特定的示例文件可以帮助您。

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