简体   繁体   English

在C中扩展ruby - 如何指定默认参数值来运行?

[英]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_methodargc的负值来做到这一点。

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_methodargc 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有许多优点:

  1. It handles optional arguments by assigning them 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传递给其中一个可选参数,这产生防止扩展程序奇怪行为的副作用。
  2. It uses 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) )。
  3. It's usually shorter. 它通常更短。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM