2011-10-02 1 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) 사용에 대한 생각을하지만 난 그냥 거기에서 세그먼테이션 폴트 (segfault)를 얻는다. README.EXT을 통해 읽는 것은 argc의 값을 사용하여 rb_define_method을 통해이를 수행 할 수 있다고 믿습니다. 그러나 이것은 너무 명확하지 않습니다. 어떤 아이디어? 감사.

답변

29

맞아요 - rb_define_method을 사용하고 음수는 argc 일 수 있습니다.

일반적으로 argc은 메서드에서 허용하는 인수의 개수를 지정하지만 음수 값을 사용하면 메서드가 Ruby가 배열로 전달할 가변 개수의 인수를 허용하도록 지정합니다.

두 가지 가능성이 있습니다. 먼저, 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; 
} 

대안은 루비 배열이 방법으로 통과 할 수있는 당신을 rb_define_method으로 전화 할 때 -2을 사용하여 지정하십시오. 이 경우 메서드는 VALUE func(VALUE obj, VALUE args)과 같은 서명이 있어야합니다. 여기서 obj은 수신 객체 (self)이고 args은 인수가 포함 된 Ruby 배열입니다. 당신은 argcrb_define_method의를 사용해야합니까

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

좋은 쓰기까지, 나는 두 번 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. 그것은 (C에서 Qnil) 그들에게 nil를 지정하여 선택적 인수를 처리합니다. 누군가 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