Вызов функции Ruby из C ++ в цикле приводит к «слишком глубокому уровню стека» - PullRequest
2 голосов
/ 03 ноября 2010

Я пытаюсь запустить блоки кода 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 );
}

1 Ответ

2 голосов
/ 05 ноября 2010

ОК, поэтому я сократил ее, пока не получил рабочую версию, похожую на мою реализацию C, но проблема не имеет ничего общего с C ++ против C. То, что не показано выше (потому что я не думал, что это актуально), заключается в том это приложение Qt, и у меня есть два слота - один для инициализации ruby ​​и загрузки программы, а другой для вызова функции цикла. Последний слот вызывается повторно из таймера (не актуально). Прорыв произошел, когда я переместил ruby_init () из слота в main (). Поиск в Google показал этот интересный ответ :

От самого Маца: «Ни один объект Ruby не должен ссылаться из области стека ниже, чем позиция во время вызова ruby_init (). "

Итак, что происходит, когда каждый слот вызывается, они находятся в неопределенном месте в стеке, в то время как если вы вызовете ruby_init () из main () и затем запустите цикл обработки событий Qt, вы обязательно будете правильное место в стеке для выполнения rb_funcall () и т. д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...