我正在尝试从C ++运行Ruby代码块。我有两个Ruby函数,一个叫做Init(),另一个叫做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
我在C ++中实现Panel是:
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.我做错了什么?
根据下面的响应,还有一些代码 - 这是为了给Python API调用提供C ++方法(期望C风格的函数):
typedef VALUE (RubyMethod)(...);
extern "C" /*static*/ VALUE StaticRubyNew( VALUE self )
{
return MainWidget::M_this->RubyNew( self );
}
答案 0 :(得分:1)
好的,所以我把它减少了,直到我得到一个类似于我的C实现的工作版本,但问题与C ++ vs C无关。上面没有显示的内容(因为我认为它不相关)是这是一个Qt应用程序,我有两个插槽 - 一个用于初始化ruby并加载程序,另一个用于调用循环函数。从计时器(不相关)重复调用后一个时隙。当我将ruby_init()移出插槽并进入main()时,突破发生了。在Google上进行的搜索显示了这个有趣的answer:
来自Matz本人,“没有Ruby对象应该从堆栈区域引用低于 ruby_init()调用时的位置。“
所以发生的事情是当每个槽被调用时,它们位于堆栈的不确定位置,而如果你从main()调用ruby_init()然后开始运行Qt的事件循环,你肯定会在堆栈上的正确位置是做rb_funcall()等。