2011-10-02 11 views
16

私は、クラスを生成するルビのC拡張を記述しようとしています。私はクラスにいくつかのデフォルトの引数を定義する方法を探しています。例えば、私はルビーで、このクラスdeclerationを持っている場合:Cでルビを拡張する - 関数のデフォルト引数値を指定する方法は?

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

あなたはmc = MyClass.new("blah")でそれを初期化することができ、年齢パラメータが内部的に設定されます。私は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); 
} 

は私がQnilに対してageの値をチェックするか、if (TYPE(age) == T_UNDEF)を使用して考えたが、私はちょうどそこからセグメンテーションフォールトを取得します。 README.EXTを読むとargcの値を使ってrb_define_methodでこれを達成できると信じていますが、これはあまり明確ではありませんでした。何か案は?ありがとう。

答えて

29

これは、rb_define_methodと負の値のargcを使用して行うことができます。

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

2つの可能性があります。まず、C配列のメソッドに引数を渡す場合は、-1を使用します。あなたのメソッドは、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; 
} 

代替はRubyの配列は、あなたのメソッドに渡さ持つことです、これを使用して、 rb_define_methodへの電話で-2を使用して指定してください。この場合、メソッドはVALUE func(VALUE obj, VALUE args)のような署名を持つ必要があります。objは受信オブジェクト(self)、argsは引数を含むRuby配列です。あなたがrb_define_methodargcを使用する必要があります

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

偉大な書き込みまでは、私はそれを2回upvoteしたい - 感謝を! – sa125

8

:あなたのケースでは、これは次のようになります。 -1argcからrb_define_methodに渡し、オプションの引数を処理するにはrb_scan_argsを使用する必要があります。 Pragmatic Bookshelf由来

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

使用

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(CでQnil)を割り当てることによって、オプションの引数を処理します。これは、誰かがを実行する任意の引数の1つにnilを渡すと、あなたの拡張機能からの奇妙な動作を防ぐという副作用があります。
  2. rb_error_arityを使用してArgumentErrorを標準形式(例: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

関連する問題