Я пытаюсь запустить блоки кода Ruby из C ++. У меня есть две функции Ruby, одна называется Init (), а другая - Loop (). У меня проблема в том, что я могу только Loop () так много раз, прежде чем я получу «слишком большой уровень стека» из SystemStackError. Насколько я могу судить, мой код Ruby не является рекурсивным. Как вы можете видеть, пока этот код на Ruby предназначен только для проверки концепции и загружен не чем иным, как стилем отладки и мигающим светом на панели. Вот код Ruby:
def Init()
puts 'Hello from script\'s Init()!'
$i = 0
$p = Panel.new
$p.Debug
$p.Extinguish( "Running" )
$p.Illuminate( "Fault" )
end
def Loop()
puts 'Hello from Loop!' + $i.to_s
$i += 1
puts $p
$p.Debug
$p.Illuminate( "Running" ) if $i % 2 == 1
$p.Extinguish( "Running" ) if $i % 2 != 1
end
Моя реализация Panel в C ++:
ruby_init();
VALUE cPanel;
cPanel = rb_define_class( "Panel", rb_cObject );
rb_define_singleton_method( cPanel, "new", (RubyMethod*)&StaticRubyNew, 0 );
rb_define_method( cPanel, "Debug", (RubyMethod*)&StaticRubyDebug, 0 );
rb_define_method( cPanel, "Extinguish", (RubyMethod*)&StaticRubyExtinguish, 1 );
rb_define_method( cPanel, "Illuminate", (RubyMethod*)&StaticRubyIlluminate, 1 );
Я вызываю функции скрипта следующим образом:
rb_eval_string( program );
rb_funcall( Qnil, rb_intern( "Init" ), 0, NULL );
// In a 200ms loop:
rb_funcall( Qnil, rb_intern( "Loop" ), 0, NULL );
Ничего не работало, пока я не написал (подозреваемую) реализацию new :
VALUE MainWidget::RubyNew( VALUE clas )
{
// Looks like we have to return *something* instead of Qnil, even if I
// don't have anything to wrap yet.
const char* s = "Dude";
VALUE tdata = Data_Wrap_Struct( clas, StaticRubyMark, StaticRubyFree, const_cast<char*>(s) );
return tdata;
}
RubyMark и RubyFree ничего не делают, а RubyDebug, RubyIlluminate и т. Д. Также не делают ничего примечательного для рассматриваемой проблемы.
Я пытался обернуть Init и Loop в класс как методы класса, поэтому я могу вызывать rb_funcall () с реальным получателем. Я попытался получить обратную трассировку, вызвав через rb_protect () (обратная трассировка кажется пустой). Похоже, что в Интернете нет секрета загрузки скрипта в виде строки, поэтому rb_eval_string () - предположение. rb_load_file () тоже не работает.
Почему это вызывает проблемы со стеком? Я могу отредактировать свой скрипт на Ruby, добавив или удалив код, и стек выпадает после выполнения различного числа циклов. Количество циклов, которые я могу выполнить, не имеет видимого отношения к числу строк. Если я удалю строку, я могу получить 45 петель. Если я уберу другого, я могу получить более 2000. Что я делаю не так?
Немного больше кода в свете ответа ниже - это для передачи методов C ++ вызовам Ruby API (которые ожидают функции в стиле C):
typedef VALUE (RubyMethod)(...);
extern "C" /*static*/ VALUE StaticRubyNew( VALUE self )
{
return MainWidget::M_this->RubyNew( self );
}