2010-11-02 59 views
1

我試圖從C++運行Ruby代碼塊。我有兩個Ruby函數,一個叫Init(),一個叫Loop()。我遇到的問題是,在我從SystemStackError中獲得「堆棧級別太深」之前,我只能使用Loop()很多次。據我所知,我的Ruby代碼不是遞歸的。正如你所看到的,到目前爲止,這個Ruby代碼僅僅是一個概念驗證,並且只加載了調試風格的東西,並在面板上閃爍。下面是Ruby代碼:在循環中調用C++中的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++實現面板的是:

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); 

沒有工作,直到我寫了一個(犯罪嫌疑人)執行

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()來獲得回溯(backtrace顯示爲空)。在線沒有任何東西似乎有加載腳本作爲字符串的祕密,所以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); 
} 
+0

嗯,我有點接近 - 我把所有這一切都推出了純C驅動的應用程序,它至少運行500,000循環(在我殺死它之前)。這肯定是我在做一個C++到C接口的錯誤。 – Scott 2010-11-03 22:00:05

回答

1

OK,所以我縮減下來,直到我得到了一個類似於我的C實現的工作版本,但問題與C++ vs C無關。上面沒有顯示的(因爲我認爲它沒有關聯)是因爲這是一個Qt應用程序,並且我有兩個插槽 - 一個初始化紅寶石並加載程序,另一個調用循環函數。後一個時隙從定時器重複調用(不相關)。當我將ruby_init()移出插槽並進入main()時發生了突破。搜索谷歌透露了這一有趣的answer

從馬茨自己,「不Ruby對象應該從堆棧區比ruby_init()調用的時間 位置較低的簡稱。」因此,當每個插槽被調用時,它們都處於堆棧的不確定位置,而如果您從main()調用ruby_init()然後開始運行Qt的事件循環,那麼您一定會在堆棧上的正確位置做rb_funcall()等

+0

順便提一句,有誰知道爲什麼Ruby有這個規則?爲什麼堆棧相對於其他調用的位置如此重要?你甚至可以編寫依賴堆棧相對於其他調用的代碼(請記住,ruby_init()在調用rb_funcall之前返回,所以它們不應該同時位於堆棧上)? – Scott 2010-11-05 15:09:41

+0

稍後在您引用的線程中:「Ruby需要知道堆棧的大小和位置,以便GC可以標記堆棧中的對象。」和「Ruby需要知道C棧的位置,所以它可以被標記,否則擴展會崩潰。」他們是否與Ruby堆棧混合使用C堆棧?這讓我覺得這是一個不必要的嘗試,聰明而且可能是虛假的懶惰。 – 2011-01-25 23:59:38

相關問題