2011-10-02 15 views
16

Bir sınıf oluşturacak ruby'e bir C uzantısı yazmaya çalışıyorum. Bazı varsayılan argümanları bir sınıfa nasıl tanımlayacağım. Örneğin, ben Yakut bu sınıf beyanınıgeçersiz varsa:C içinde rubyeni genişletme - varsayılan argüman değerleri işlevinin nasıl belirtileceği?

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

Sen mc = MyClass.new("blah") ile başlatabilir ve yaş parametresi dahili olarak ayarlanacak. Bunu C'de nasıl yapabilirim? Şimdiye kadar bu var, ama bu güçler diğer argüman girme:

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

Ben Qnil karşı age değerini kontrol eden veya if (TYPE(age) == T_UNDEF) kullanmayı düşündünüz, ama sadece oradan çalışma sırasında parçalama arızası olsun. README.EXT aracılığıyla okuma, rb_define_method aracılığıyla argc değerini kullanarak bunu başarabileceğime inanmamı sağlıyor, ancak bu çok açık değildi. Herhangi bir fikir? Teşekkürler.

cevap

29

Haklısınız - bunu rb_define_method kullanarak ve argc için negatif bir değerle yapabilirsiniz.

Normalde argc yönteminizin kabul ettiği bağımsız değişkenlerin sayısını belirtir, ancak negatif bir değer kullanmak, yöntemin Ruby'nin bir dizi olarak ileteceği değişken sayıda bağımsız değişken kabul ettiğini belirtir.

İki olasılık vardır. İlk olarak, argümanların bir C dizisinde yönteminize iletilmesini istiyorsanız, -1'u kullanın. Sizin yönteminiz VALUE func(int argc, VALUE *argv, VALUE obj) gibi bir imzaya sahip olacaktır, burada argc argümanların sayısıdır, argv argümanların kendileri için bir göstericidir ve obj alıcı nesnedir, yani self. Varsayılan argümanları taklit etmek için istediğiniz zaman bu dizi işleyebilirsiniz ya da durumunda bu gibi görünebilir, ne gerekiyorsa:

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

alternatif bir Ruby dizisi yönteminiz, içine geçmiş etmek hangi sizi Aramanıza -2 kullanarak, rb_define_method numaralı telefonu kullanarak belirtin. Bu durumda, yönteminizin, obj alma nesnesi (self) olduğu ve args argümanlarını içeren bir Ruby dizisi olduğu VALUE func(VALUE obj, VALUE args) gibi bir imzanın olması gerekir. Sen argcrb_define_method kullanımı gerekiyor

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

büyük yazma yukarı, ben iki kez upvote ediyorum - teşekkürler! – sa125

8

: Senin durumunda, bu gibi görünebilir. -1'u argc olarak rb_define_method'a geçirmeniz ve isteğe bağlı bağımsız değişkenleri işlemek için rb_scan_args kullanmanız gerekir. Pragmatic Bookshelf türetilen

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

Kullanımı

:

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. 

Örnek:

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

Bundan başka, örneğin, mat örneğinin aşağıdaki basitleştirilebilir Ruby 2, ayrıca birvar Adlandırılmış argümanlar ve karma seçenekleri için kullanılanbayrağı. Ancak, nasıl çalıştığını henüz anlamadım.

Neden?

rb_scan_args kullanmanın pek çok avantajı vardır:

  1. O (C Qnil) onları nil atayarak isteğe bağlı argümanlar yönetir. Bu, birisi nil'u 'un gerçekleştirdiği isteğe bağlı argümanlardan birine geçirirse, uzantılarınızdan gelen tuhaf davranışları önlemenin yan etkisine sahiptir.
  2. Standart biçimde (örn. wrong number of arguments (2 for 1)) bir ArgumentError yükseltmek için rb_error_arity kullanır.
  3. Genellikle daha kısadır. rb_scan_args ait

avantajları daha da burada işlenecektir: Elimde olsa http://www.oreillynet.com/ruby/blog/2007/04/c_extension_authors_use_rb_sca_1.html

İlgili konular