[英]Extending ruby in C - how to specify default argument values to function?
I'm trying to write a C extension to ruby that'll generate a class. 我正在尝试为ruby写一个C扩展,它将生成一个类。 I'm looking on how to define some default arguments to a class.
我正在研究如何为类定义一些默认参数。 For example, if I have this class decleration in ruby:
例如,如果我在ruby中有这个类的decleration:
class MyClass
def initialize(name, age=10)
@name = name
@age = age
end
end
You can initialize it with mc = MyClass.new("blah")
, and the age parameter will be set internally. 您可以使用
mc = MyClass.new("blah")
对其进行初始化,并在内部设置age参数。 How do I do this in C? 我怎么用C做这个? So far I got this, but this forces entering the other argument:
到目前为止,我得到了这个,但这迫使进入另一个论点:
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);
}
I thought about checking the value of age
against Qnil
or using if ( TYPE(age) == T_UNDEF )
, but I just get segfaults from there. 我考虑过针对
Qnil
检查age
的值或使用if ( TYPE(age) == T_UNDEF )
,但我只是从那里得到段错误。 Reading through README.EXT
leads me to believe I can accomplish this through rb_define_method
using the value of argc
, but this wasn't too clear. 阅读
README.EXT
让我相信我可以通过rb_define_method
使用argc
的值来实现这一目标,但这不太清楚。 Any ideas? 有任何想法吗? Thanks.
谢谢。
You're right - you can do this using rb_define_method
and a negative value for argc
. 你是对的 - 你可以使用
rb_define_method
和argc
的负值来做到这一点。
Normally argc
specifies the number of arguments your method accepts, but using a negative value specifies that the method accepts a variable number of arguments, which Ruby will pass in as an array. 通常,
argc
指定方法接受的参数数量,但使用负值指定方法接受可变数量的参数,Ruby将作为数组传入。
There are two possibilities. 有两种可能性。 First, use
-1
if you want the arguments passed in to your method in a C array. 首先,如果希望将参数传入C方法中的方法,请使用
-1
。 Your method will have a signature like VALUE func(int argc, VALUE *argv, VALUE obj)
where argc
is the number of arguments, argv
is a pointer to the arguments themselves, and obj is the receiving object, ie self
. 你的方法将有一个像
VALUE func(int argc, VALUE *argv, VALUE obj)
这样的签名VALUE func(int argc, VALUE *argv, VALUE obj)
其中argc
是参数的数量, argv
是参数本身的指针,而obj是接收对象,即self
。 You can then manipulate this array as you need to mimic default arguments or whatever you need, in your case it might look something like this: 然后你可以操作这个数组,因为你需要模仿默认的参数或你需要的任何东西,在你的情况下,它可能看起来像这样:
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;
}
The alternative is to have a Ruby array passed into your method, which you specify by using -2
in your call to rb_define_method
. 另一种方法是将一个Ruby数组传递给您的方法,您可以在调用
rb_define_method
使用-2
指定。 In this case, your method should have a signature like VALUE func(VALUE obj, VALUE args)
, where obj
is the receiving object ( self
), and args
is a Ruby array containing the arguments. 在这种情况下,您的方法应该有一个签名,如
VALUE func(VALUE obj, VALUE args)
,其中obj
是接收对象( self
), args
是包含参数的Ruby数组。 In your case this might look something like this: 在您的情况下,这可能看起来像这样:
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;
}
You do need to use the argc
of rb_define_method
. 您需要使用
rb_define_method
的argc
。 You should pass -1
as the argc
to rb_define_method
and use rb_scan_args
to handle optional arguments. 您应该将
-1
作为argc
传递给rb_define_method
并使用rb_scan_args
来处理可选参数。 For example, matt's example could be simplified to the following: 例如,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;
}
Usage 用法
Derived from the Pragmatic Bookshelf : 源自实用书架 :
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.
Example: 例:
VALUE name, one, two, rest;
rb_scan_args(argc, argv, "12", &name, &one, &two);
rb_scan_args(argc, argv, "1*", &name, &rest);
Furthermore, in Ruby 2, there is also a :
flag that is used for named arguments and the options hash. 此外,在Ruby 2中,还有一个
:
标志,用于命名参数和选项哈希。 However, I have yet to figure out how it works. 但是,我还没弄清楚它是如何工作的。
Why? 为什么?
There are many advantages of using rb_scan_args
: 使用
rb_scan_args
有许多优点:
nil
( Qnil
in C). nil
(C中的Qnil
)来处理可选参数。 This has the side effect of preventing odd behaviour from your extension if someone passes nil
to one of the optional arguments, which does happen. nil
传递给其中一个可选参数,这会产生防止扩展程序奇怪行为的副作用。 rb_error_arity
to raise an ArgumentError in the standard format (ex. wrong number of arguments (2 for 1)
). rb_error_arity
以标准格式引发ArgumentError (例如, wrong number of arguments (2 for 1)
)。 The advantages of rb_scan_args
are further elaborated here: http://www.oreillynet.com/ruby/blog/2007/04/c_extension_authors_use_rb_sca_1.html rb_scan_args
的优点在rb_scan_args
进一步阐述: http : //www.oreillynet.com/ruby/blog/2007/04/c_extension_authors_use_rb_sca_1.html
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.