2011-10-02 15 views
16

मैं रूबी के लिए एक सी एक्सटेंशन लिखने की कोशिश कर रहा हूं जो कक्षा उत्पन्न करेगा। मैं देख रहा हूं कि कक्षा में कुछ डिफ़ॉल्ट तर्क कैसे परिभाषित करें। उदाहरण के लिए, अगर मैं रूबी में इस वर्ग के decleration है:सी में रूबी विस्तार - कार्य करने के लिए डिफ़ॉल्ट तर्क मान निर्दिष्ट करने के लिए कैसे?

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

आप इसे mc = MyClass.new("blah") साथ प्रारंभ कर सकते हैं, और उम्र पैरामीटर आंतरिक रूप से स्थापित किया जाएगा। मैं सी में यह कैसे करूँ? अब तक मैं यह मिल गया है, लेकिन इस बलों अन्य तर्क में प्रवेश:

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) का उपयोग कर के बारे में सोचा है, लेकिन मैं तो बस वहाँ से segfaults मिलता है। README.EXT के माध्यम से पढ़ना मुझे विश्वास दिलाता है कि मैं इसे rb_define_method के माध्यम से argc के मान का उपयोग करके पूरा कर सकता हूं, लेकिन यह बहुत स्पष्ट नहीं था। कोई विचार? धन्यवाद।

उत्तर

29

आप सही हैं - आप इसे rb_define_method और argc के लिए नकारात्मक मान का उपयोग करके कर सकते हैं।

आम तौर पर argc आपकी विधि स्वीकार करने वाले तर्कों की संख्या निर्दिष्ट करता है, लेकिन नकारात्मक मान का उपयोग करके यह निर्दिष्ट करता है कि विधि तर्कों की एक चर संख्या को स्वीकार करती है, जो रूबी एक सरणी के रूप में गुजरती है।

दो संभावनाएं हैं। सबसे पहले, यदि आप सी सरणी में अपनी विधि में दिए गए तर्क चाहते हैं तो -1 का उपयोग करें। आपकी विधि में VALUE func(int argc, VALUE *argv, VALUE obj) जैसे हस्ताक्षर होंगे जहां argc तर्कों की संख्या है, argv स्वयं तर्कों के लिए एक सूचक है, और ओबीजे प्राप्त करने वाला ऑब्जेक्ट है, यानी 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 तर्क युक्त रूबी सरणी है।

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

महान लिखने अप, मैं इसे दो बार करता है, तो मैं कर सकता वोट दें चाहते हैं - धन्यवाद! – sa125

8

आप rb_define_method की argc उपयोग करने की आवश्यकता है: आपके मामले में यह कुछ इस तरह लग सकता है। आपको -1argc से 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); 

इसके अलावा, में उदाहरण के लिए, मैट के उदाहरण का अनुसरण करने के लिए सरल किया जा सकता है रूबी 2,भी हैध्वज जिसका नाम नामित तर्कों और विकल्प हैश के लिए उपयोग किया जाता है। हालांकि, मुझे अभी तक यह पता लगाना नहीं है कि यह कैसे काम करता है।

क्यों?

rb_scan_args का उपयोग कर के कई फायदे हैं:

  1. यह उन्हें nil बताए (Qnil सी में) द्वारा वैकल्पिक तर्क संभालती है। यदि कोई nil वैकल्पिक तर्कों में से किसी एक को पास करता है, तो होता है, तो इसका आपके एक्सटेंशन से अजीब व्यवहार को रोकने का दुष्प्रभाव होता है।
  2. यह मानक प्रारूप में ArgumentError बढ़ाने के लिए rb_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