Как передать ключевые слова методу Ruby из расширения C?

Я работаю над расширением C для Ruby и хочу вызвать метод с обязательными аргументами ключевого слова, например:

class Word 
  def initialize(line:, col:, value:)
  end 
end 

В C я знаком с вызовом методов Ruby через rb_funcall и rb_funcallv, но я не могу понять, как передавать аргументы ключевого слова!

Вот несколько вещей, которые я пробовал:

Передайте хеш в качестве последнего позиционного аргумента с rb_funcall:

VALUE kwargs = rb_hash_new();
rb_hash_aset(kwargs, rb_intern("name"), rb_str_new2(name));
// ... 
rb_funcall(Word_Class, rb_intern("new"), 1, kwargs);
// result: ArgumentError: wrong number of arguments (given 1, expected 0)

Передайте его как последний член массива argv с rb_funcallv:

// initialize `kwargs` as above
VALUE argv = rb_ary_new();
rb_ary_push(argv, kwargs);
rb_funcallv(Word_Class, rb_intern("new"), 1, &argv);
// result: ArgumentError: wrong number of arguments (given 1, expected 0)

Передайте 0 как argc, несмотря на то, что argv имеет длину 1:

// initialize `argv` as above
rb_funcallv(Word_Class, rb_intern("new"), 0, &argv);
// ArgumentError: missing keywords: line, col, value

Является ли это возможным? Как это делается? Есть ли что-нибудь еще, что я могу попробовать?


person rmosolgo    schedule 29.10.2016    source источник
comment
Думаю, вы должны иметь возможность просто передать хэш (rb_hash_new для его создания, rb_hash_aset для установки значений). Дайте мне знать, если это сработает, чем я могу превратить это в ответ.   -  person Michael Kohl    schedule 29.10.2016
comment
Спасибо за ваше предложение! Я обновил вопрос, указав несколько вещей, которые я пробовал ... есть ли другой способ передать хэш в качестве ключевых слов?   -  person rmosolgo    schedule 29.10.2016
comment
Вы находитесь на правильном пути с первой попытки (передача хеша в качестве последнего аргумента), но ключи должны быть символами, а не идентификаторами. Попробуйте ID2SYM(rb_intern("name")), а не просто rb_intern("name"). Я не знаю, есть ли способ конвертировать char * в C непосредственно в рубиновый символ за один шаг, похоже, вам нужно создать идентификатор, а затем преобразовать в символ.   -  person matt    schedule 29.10.2016
comment
@мат, это работает! Не могли бы вы добавить это предложение в качестве ответа, чтобы я мог принять его и дать вам волшебные точки переполнения стека?   -  person rmosolgo    schedule 29.10.2016
comment
Я думаю, что @MichaelKohl имеет приоритет в получении этих очков.   -  person matt    schedule 30.10.2016
comment
Спасибо @мат. Я отправил ответ сейчас.   -  person Michael Kohl    schedule 31.10.2016


Ответы (1)


Вы можете пройти в хеше. Обратите внимание, что для создания ключей символов вам нужен вызов формы ID2SYM(rb_intern(char*)), так как rb_intern возвращает ID, который ID2SYM превращается в фактический символ Ruby.

VALUE kwargs = rb_hash_new();
rb_hash_aset(kwargs, ID2SYM(rb_intern("name")), rb_str_new2(name));
// ... 
rb_funcall(Word_Class, ID2SYM(rb_intern("new")), 1, kwargs);
person Michael Kohl    schedule 31.10.2016