sa1*_*125 16 c ruby ruby-c-extension
我正在尝试为ruby写一个C扩展,它将生成一个类.我正在研究如何为类定义一些默认参数.例如,如果我在ruby中有这个类的decleration:
class MyClass
def initialize(name, age=10)
@name = name
@age = age
end
end
Run Code Online (Sandbox Code Playgroud)
您可以使用它进行初始化mc = MyClass.new("blah"),并在内部设置age参数.我怎么用C做这个?到目前为止,我得到了这个,但这迫使进入另一个论点:
require "ruby.h"
static VALUE my_init(VALUE self, VALUE name, VALUE age)
{
rb_iv_set(self, "@name", name);
rb_iv_set(self, "@age", age);
return self;
}
VALUE cMyClass;
void Init_MyClass()
{
// create a ruby class instance
cMyClass = rb_define_class("MyClass", rb_cObject);
// connect the instance methods to the object
rb_define_method(cMyClass, "initialize", my_init, 2);
}
Run Code Online (Sandbox Code Playgroud)
我考虑过检查age反对Qnil或使用的价值if ( TYPE(age) == T_UNDEF ),但我只是从那里得到段错误.阅读README.EXT让我相信我可以通过rb_define_method使用价值来实现这一目标argc,但这并不是很清楚.有任何想法吗?谢谢.
mat*_*att 29
你是对的 - 你可以使用rb_define_method和负值argc.
通常argc指定方法接受的参数个数,但使用负值指定方法接受可变数量的参数,Ruby将作为数组传入.
有两种可能性.首先,-1如果您希望将参数传递给C数组中的方法,请使用.您的方法将有一个签名等VALUE func(int argc, VALUE *argv, VALUE obj),其中argc是参数的数量,argv是一个指针参数本身,和obj为接收对象,即,self.然后,您可以操作此数组,因为您需要模拟默认参数或您需要的任何内容,在您的情况下,它可能看起来像这样:
static VALUE my_init(int argc, VALUE* argv, VALUE self) {
VALUE age;
if (argc > 2 || argc == 0) { // there should only be 1 or 2 arguments
rb_raise(rb_eArgError, "wrong number of arguments");
}
rb_iv_set(self, "@name", argv[0]);
if (argc == 2) { // if age has been included in the call...
age = argv[1]; // then use the value passed in...
} else { // otherwise...
age = INT2NUM(10); // use the default value
}
rb_iv_set(self, "@age", age);
return self;
}
Run Code Online (Sandbox Code Playgroud)
另一种方法是将Ruby数组传递给您的方法,您可以-2在调用时使用该数组指定rb_define_method.在这种情况下,你的方法应当具有类似的签名VALUE func(VALUE obj, VALUE args),其中,obj是接收对象(self),并且args是包含参数的红宝石阵列.在您的情况下,这可能看起来像这样:
static VALUE my_init(VALUE self, VALUE args) {
VALUE age;
long len = RARRAY_LEN(args);
if (len > 2 || len == 0) {
rb_raise(rb_eArgError, "wrong number of arguments");
}
rb_iv_set(self, "@name", rb_ary_entry(args, 0));
if (len == 2) {
age = rb_ary_entry(args, 1);
} else {
age = INT2NUM(10);
}
rb_iv_set(self, "@age", age);
return self;
}
Run Code Online (Sandbox Code Playgroud)
你需要使用argc的rb_define_method.您应该-1作为argcto 传递rb_define_method并使用它rb_scan_args来处理可选参数.例如,matt的示例可以简化为以下内容:
static VALUE my_init(int argc, VALUE* argv, VALUE self) {
VALUE name, age;
rb_scan_args(argc, argv, "11", &name, &age); // informs ruby that the method takes 1 mandatory and 1 optional argument,
// the values of which are stored in name and age.
if (NIL_P(age)) // if no age was given...
age = INT2NUM(10); // use the default value
rb_iv_set(self, "@age", age);
rb_iv_set(self, "@name", name);
return self;
}
Run Code Online (Sandbox Code Playgroud)
用法
源自实用书架:
int rb_scan_args (int argcount, VALUE *argv, char *fmt, ...
Scans the argument list and assigns to variables similar to scanf:
fmt A string containing zero, one, or two digits followed by some flag characters.
The first digit indicates the count of mandatory arguments; the second is the count of optional arguments.
A * means to pack the rest of the arguments into a Ruby array.
A & means that an attached code block will be taken and assigned to the given variable
(if no code block was given, Qnil will be assigned).
After the fmt string, pointers to VALUE are given (as with scanf) to which the arguments are assigned.
Run Code Online (Sandbox Code Playgroud)
例:
VALUE name, one, two, rest;
rb_scan_args(argc, argv, "12", &name, &one, &two);
rb_scan_args(argc, argv, "1*", &name, &rest);
Run Code Online (Sandbox Code Playgroud)
此外,在Ruby 2中,还有一个:用于命名参数和选项哈希的标志.但是,我还没弄清楚它是如何工作的.
为什么?
使用有许多优点rb_scan_args:
nil(Qnil在C中).如果有人传递nil给其中一个可选参数,这会产生防止扩展程序出现异常行为的副作用.rb_error_arity以标准格式引发ArgumentError(例如wrong number of arguments (2 for 1)).这里的优点rb_scan_args进一步详述:http://www.oreillynet.com/ruby/blog/2007/04/c_extension_authors_use_rb_sca_1.html
| 归档时间: |
|
| 查看次数: |
3429 次 |
| 最近记录: |