17

クラスを生成する C 拡張を ruby​​ に書こうとしています。クラスにいくつかのデフォルト引数を定義する方法を探しています。たとえば、Ruby でこのクラス宣言があるとします。

class MyClass
  def initialize(name, age=10)
    @name = name
    @age  = age
  end
end

で初期化でき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);
}

の値をチェックするか、を使用することを考えましたageQnilif ( TYPE(age) == T_UNDEF )そこから segfault が発生します。読んでみると、 の値を使用してREADME.EXTこれを達成できると思いますが、これはあまり明確ではありませんでした。何か案は?ありがとう。rb_define_methodargc

4

2 に答える 2

31

rb_define_methodその通りです。と に負の値を使用してこれを行うことができますargc

通常argc、メソッドが受け入れる引数の数を指定しますが、負の値を使用すると、Ruby が配列として渡す可変数の引数をメソッドが受け入れることを指定します。

2 つの可能性があります。まず、-1引数を C 配列でメソッドに渡したい場合に使用します。メソッドには、VALUE func(int argc, VALUE *argv, VALUE obj)whereargcは引数の数、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;
}

もう 1 つの方法は、Ruby 配列をメソッドに渡すことです。これ-2は、 への呼び出しで を使用して指定しますrb_define_method。この場合、メソッドには のような署名が必要ですVALUE func(VALUE obj, VALUE args)。ここobjで、 は受信オブジェクト ( self) であり、argsは引数を含む Ruby 配列です。あなたの場合、これは次のようになります。

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;
}
于 2011-10-02T15:29:58.853 に答える
8

argcofを使用する必要がありますrb_define_method-1toとして渡し、オプションの引数を処理するargcためrb_define_methodに使用する必要があります。rb_scan_argsたとえば、マットの例は次のように簡略化できます。

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;
}

使用法

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.

例:

VALUE name, one, two, rest;
rb_scan_args(argc, argv, "12", &name, &one, &two);
rb_scan_args(argc, argv, "1*", &name, &rest);

さらに、Ruby 2 では、:名前付き引数とオプション ハッシュに使用されるフラグもあります。ただし、それがどのように機能するかはまだわかりません。

なんで?

を使用することには多くの利点がありますrb_scan_args

  1. オプションの引数を割り当てることで処理しますnil( QnilC では)。nilこれには、誰かがオプションの引数の 1 つに渡された場合に、拡張機能から奇妙な動作発生するのを防ぐという副作用があります。
  2. 標準形式 (例) でArgumentErrorrb_error_arityを発生させるために使用します。wrong number of arguments (2 for 1)
  3. 通常はもっと短いです。

の利点についてrb_scan_argsは、こちらで詳しく説明しています: http://www.oreillynet.com/ruby/blog/2007/04/c_extension_authors_use_rb_sca_1.html

于 2013-11-24T16:29:31.183 に答える