Вы правы - вы можете сделать это, используя rb_define_method
и отрицательное значение для argc
.
Обычно argc
указывает количество аргументов, которые принимает ваш метод, но использование отрицательного значения указывает, что метод принимает переменное количество аргументов, которые Ruby передает в виде массива.
Есть две возможности. Во-первых, используйте -1
, если вы хотите, чтобы аргументы передавались вашему методу в массиве C. Ваш метод будет иметь подпись, такую как 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 в ваш метод, который вы указываете с помощью -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;
}