精華區beta Ruby 關於我們 聯絡資訊
http://www.swig.org/ SWIG is an interface compiler that connects programs written in C and C++ with scripting languages such as Perl, Python, Ruby, and Tcl. 也就是,讓 C/C++ 與 scripting language 溝通的介面產生器 目前支援 13 種 scripting language: * Allegro Common Lisp * C# * Chicken * Guile * Java * Modula-3 * Mzscheme * OCAML * Perl * PHP * Python * Ruby * Tcl 之前我測了一些 Ruby 與 C++ 的溝通方式,以下將簡單介紹一下測試的方法 首先先看到使用方式,我想這個應該是最重要的部份 畢竟如果使用不便,那其他的也不用再多說什麼了… (以下由於 BBS 之故,稍作排版修改) 以下這個程式是一個簡單的 type wrapper,(傳值版) 由 template parameter 指定型別, data() 當 getter, data(type) 當 setter. 另外在 c'tor 和 d'tor 中插入 IO 來追蹤物件的生成與摧毀 // in Wrapper.h #include <iostream> template <class Data> class Wrapper{ public: Wrapper(Data new_data): data_(new_data){ std::cout << "C++: Wrapper is created, which is '" << data_ << "' .\n"; } ~Wrapper(){ std::cout << "C++: Wrapper is decayed, which is '" << data_ << "' .\n"; } Data data() const{ return data_; } void data(Data new_data){ data_ = new_data; } private: Data data_; }; 有了這個 .h 檔的介面後(這裡同時也包含實作), 接下來我們需要的是一個由 swig 產生的膠水,將 C++ 介面與 Ruby 介面混合 暫時命名為:Wrapper.i, 詳細做法等一下我們再來看 總之最終我們將產生一個動態連結檔,也就是一個 Ruby 的 module, 叫 Wrapper 不過需要一提的是,由於 Ruby 本身不支援 template, 所以我們在 Wrapper.i 中必須明確指出我們需要什麼型別, 否則 Ruby 會無法使用(更明確來講,純 template 根本無法 compile) 所以在 Wrapper.i 中,裡面有一行是這個: %template(Integer) Wrapper<int>; 這句話的意思是,將 Wrapper<int> 這個型別取作 Integer 於是 Ruby module 做好後,可以使用 Integer 這個型別 雖然我中間還有很多測試,不過直接看目前的最後結果吧 寫一個 Ruby 程式,內容如下: #!ruby # in test.rb require 'Wrapper' # 讀入剛剛做好的 Wrapper module class Test < Wrapper::Integer # 繼承 C++ 寫好的 Wrapper<int> def initialize(new_data) # c'tor 其實可以省略,因為可以使用 super(new_data) # C++ 中寫好的 Wrapper<int> end # 這裡只是展示 super 是可以用的 def plus(num) data(data + num) data end end # 正式測試開始 fat = Test.new(123) # 建構出 Test 物件,傳入 123 當引數 puts fat.plus(7) # output: 130 fat.data(456) # 設值為 456 puts fat.plus(4) # output: 460 ok, 可是原本插入 C++ 的 c'tor 中的 IO 呢? 整個程式的輸出結果是這樣: C++: Wrapper is created, which is '123' . 130 460 C++: Wrapper is decayed, which is '460' . 也就是說,C++ 的 c'tor 與 d'tor 有被確實喚起 這樣說不定可以替 Ruby 實作出 d'tor...不過這是題外話 另外,剛剛程式中的 fat.data 與 fat.data(456) 確實是 喚起正確的 getter 與 setter, 可是 Ruby 沒有 overload? 我沒有實際去看,但我猜他內部大概是這樣實作的: def data(*args) case args.size when 0 then return self.data_a when 1 then return self.data_b(args[0]) end end 所以如果你想要重新定義其中一個 overloaded method, 做不到… 可能必須找到他實際的名字才有辦法 那麼我們再來看到由 C++ 呼喚 Ruby... 否則不就太寂寞了嗎? 基本上 Ruby 本身就提供了良好與 C 溝通的機制,照用不就好了? 是這樣說沒錯,可是說真的我很討厭 C 的介面…囧rz 常常會要你記下一堆有的沒的,感覺很討厭 所以我覺得需要一個 C++ 的 wrapper, 一個可以輕鬆呼叫 Ruby 的方式 像上面推文中所提到的,我找到這個: http://www.sourcepole.com/sources/software/c++ruby/ 不過這個東西實在是寫得不好,沒有處理 finalize 的部份 不知道還有沒有其他類似的 wrapper 可以用,所以我就暫時用這個來改 改寫他 singleton 的實作,還有記得在 d'tor 中呼叫 ruby_finalize(); 最後的結果是,我可以這樣寫: #include "rubyeval.h" // 就是上面抓來的那個 #include <ruby.h> // embedded ruby 必要的東西,去 ruby-lang 就可以抓 int main(){ RubyEval& ruby = RubyEval::instance(); ruby.eval("require 'Wrapper'"); // 直接用字串執行 Ruby // 這邊是含入剛剛做好的 C++ => Ruby mod // 當然這東西要先 compile 好 ruby.eval("fat = Wrapper::Integer.new(123)"); // 直接建立物件 std::cout << NUM2INT(ruby.eval("fat.data")) << std::endl; // 輸出 123 ruby.eval("fat.data(456);"); // 設值 456 std::cout << NUM2INT(ruby.eval("fat.data")) << std::endl; // 輸出 456 ruby.run_file("test.rb"); // 直接執行 ruby 程式 } 那個 test.rb, 就是上面寫好拿來測試 Ruby 呼叫 C++ 的程式 於是整個輸出結果就是: C++: Wrapper is created, which is '123' . 123 456 C++: Wrapper is created, which is '123' . 130 460 C++: Wrapper is decayed, which is '460' . C++: Wrapper is decayed, which is '456' . 第一行是從 C++ 建構的 Integer; 123, 456 則是 cout 輸出的 第四行的 123 則是 test.rb 產生 class Test < Wrapper::Integer 那個 130, 460 則是 Ruby 的 puts 產生出來的 最後兩個 decayed 則是 Ruby 的 gc 正確在程式結束時摧毀物件輸出的 順序剛好跟 c'tor 反過來,一切正常 最後就來看怎麼實現這個的 Wrapper.i 是這樣寫的: %module Wrapper %{ #include "Wrapper.h" %} %include "Wrapper.h" %template(Integer) Wrapper<int>; 這些語法請參考 SWIG 的網站,那邊都有詳細說明 甚至是 C++ 的多重繼承,在 Ruby 中也能使用 當然多少可能會有點限制,但似乎可以實現一定的功能 下指令: swig -c++ -ruby Wrapper.i 這樣就可以以 Wrapper.i 這個介面檔實作出由 Ruby 溝通 C++ 的程式 那個程式會叫做 Wrapper_wrap.cxx, 也就是 YOUR_NAME_wrap.cxx 再來就是將所有的程式打包成一個動態連結檔了,所有的檔案是: Wrapper.h (包含實作), Wrapper_wrap.cxx (SWIG 的膠水) 方便的做法是使用 Ruby 的 mod, 叫 mkmf 寫一個 Ruby 程式叫 mkmf.rb, 內容是: require 'mkmf' $libs = append_library($libs, "stdc++") create_makefile(ARGV[0]) 這邊是由於我個人方便,所以這樣寫的 第一行含入 mkmf, 第二行是因為我用到 C++ 標準函數庫(cout), 所以必須連結 stdc++ 才行(我的系統是 gcc, VC++ 的話我不清楚) 接著由 create_makefile 產生出我要的 makefile, 名稱由 cmd line 輸入 這邊我是輸入 Wrapper (btw, 有人知道 stdc++ 可否動態連結嗎?) 接著就可以由 make 將 Wrapper.h 和 Wrapper_wrap.cxx 合併做出 Wrapper.so (我想 VC++ 系統應該會做出 Wrapper.dll 之類的) 最後要提的是,整個程式的執行環境 由於我是在 cygwin 下作業的,所以獨立執行這些程式需要的是: cygwin1.dll 1.78 MB cygruby18.dll 703 KB cygcrypt-0.dll 6.5 KB 我想如果在 windows 下由 VC++ compile 的話, 應該就只會需要 Ruby interpreter 的 dll 檔(這裡是 cygruby18.dll) 不過我就沒有做這一步的測試了,留給讀者當作練習吧 XD (忽然心血來潮所以整理了這些東西) -- Nobody can take anything away from him. Nor can anyone give anything to him. What came from the sea, has returned to the sea. Chrono Cross -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 220.135.28.18
dotZu:13種語言怎麼有Java但沒Java Script 08/06 18:53
godfat:Java 和 JavaScript 沒有因果關係 08/06 19:02
SBT:Java 和 JavaScript 完全不一樣 08/06 22:43
PsMonkey:ㄟㄟ... 把那些 html 還有論壇的 tag 去掉好唄... 08/07 22:22
godfat:其實那是我在這裡打時打上去的 XD 另一種顏文字吧 @_@ 08/07 23:21
※ 編輯: godfat 來自: 220.135.28.18 (08/07 23:25)
dotZu:二、三樓:正是因為不一樣才奇怪啊 Java 不是 Scripting 08/08 13:30
cplusplus:反正只是想要利用C\C++的效率吧? 那JAVA也可以用啊~ 08/08 14:14
cplusplus:又話說JAVASCRIPT一般在BROWSER上執行 比較少會去用這個 08/08 14:14
cplusplus:編好的MODULE吧...? 08/08 14:16