`

SWIG/Ruby

    博客分类:
  • ruby
阅读更多
以下文章最早發表於 2006.08.06, ptt Ruby 板。由於這裡的版面跟 BBS 有極大的差異,所以稍微調整了一下排版。不過程式碼的部份可能很難調整到方便閱讀,這點也就請見諒了。

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++ 的溝通方式,以下將簡單介紹一下測試的方法。首先先看到使用方式,我想這個應該是最重要的部份,畢竟如果使用不便,那其他的也不用再多說什麼了…。




以下這個程式是一個簡單的 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 程式,內容如下:
#!/usr/bin/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

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 的 lib, 叫 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

(忽然心血來潮所以整理了這些東西)

2006.08.06 godfat 真常

延伸閱讀
分享到:
评论

相关推荐

    Ruby中使用SWIG编写ruby扩展模块实例

    在使用ruby/rails的过程中,确实发现有时性能不尽人意,如生成一个拥有600项的item的3层树形结构目录要花去20ms,为提高性能在学习用c/c++写ruby模块的过程中,认识了swig,rubyInline等一系列帮助编写c/c++来提升...

    SWIG4.0开发手册

    SWIG(Simplified Wrapper and Interface Generator)是一种开源的软件开发工具,旨在帮助程序员将C或C++代码库与高级编程语言如Python、Perl、Ruby、Tcl、PHP等进行接口封装,实现代码重用和扩展。使用SWIG,可以...

    swig工具,C/C++到python, tcl 的接口

    SWIG(Simplified Wrapper and Interface Generator)是一种开源的接口生成工具,可以将C/C++语言与各种脚本语言集成,包括Python、Tcl、Perl、 Ruby等。SWIG的主要功能是生成一个 wrapper(封装器),将C/C++代码...

    SWIG 公开 C/C++ 代码,包括 Ruby、Perl、Tcl、C# 和 Python

    C 和 C++ 被公认为...SWIG 允许您向广泛的脚本语言公开 C/C++ 代码,包括 Ruby、Perl、Tcl 和 Python。本文使用 Ruby 作为公开 C/C++ 功能的首选脚本接口。要理解本文,您必须具备 C/C++ 与 Ruby 方面的相应知识。

    SWIG 源码(C/C++与其他语言的粘合剂)

    SWIG能应用于各种不同类型的语言包括常用脚本编译语言例如Perl, PHP, Python, Tcl, Ruby and PHP。支持语言列表中也包括非脚本编译语言,例如C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Modula-3, ...

    swig4.1.1linux版+windows版

    SWIG(Simplified Wrapper and Interface Generator)是一款强大的工具,用于将C和C++代码自动绑定到多种脚本语言,如Python、Perl、Ruby等。它简化了创建这些语言的接口的过程,使得C/C++库可以方便地在各种脚本...

    SWIG 2.0.11 C/C++接入各种不同语言的接口程序

    In a nutshell, SWIG is a compiler that takes C/C++ declarations and creates the wrappers needed to access those declarations from other languages including including Perl, Python, Tcl, Ruby, Guile, ...

    SWIG源码及工具

    它使得C或C++的程序可以方便地与多种高级语言,如Python、Java、Perl、Tcl、Ruby等进行交互。SWIG通过解析C或C++的头文件,生成相应的接口代码,从而让这些语言能够调用原生的C/C++函数和对象。 SWIG的核心功能在于...

    swig学习资料.doc

    - **3.2.1.7 Ruby** **3.3 SWIG 在 Cygwin 和 MinGW 上的使用** - **3.3.1 构建 swig.exe** SWIG 可以在 Cygwin 和 MinGW 环境下构建: - 使用 MinGW 和 MSYS - 使用 Cygwin - 替代方案 - **3.3.2 在 ...

    SWIG Specification

    SWIG能够在不需要修改现有代码的基础上,自动创建其他编程语言(如Perl、Python、Tcl、Ruby、Guile和Java)访问C/C++声明所需的包装器(wrappers)。这一特性极大地促进了不同语言开发者的协作与代码复用,提高了...

    cmake_c++_python_swig

    SWIG不仅支持C++,还支持许多其他语言,如Java、Perl、Ruby等。通过SWIG,开发者可以将现有的C++代码快速地包装成Python模块,无需手动编写复杂的Python-C接口代码。 **CMake与SWIG结合使用** 在CMake项目中整合...

    swig-2.0.10 .tar.gz

    SWIG(Simplified Wrapper and Interface Generator)是一款强大的工具,用于将C和C++代码与多种高级语言(如Python、Perl、Java、Ruby等)连接起来。`swig-2.0.10.tar.gz` 是SWIG的一个版本,具体是2.0.10版的源码...

    swig使用手册

    它通过自动生成必要的封装代码,使C/C++库能够被多种脚本语言调用,如Perl、Python、Ruby等,无需开发者手动编写繁琐的接口代码。 **2.1 SWIG是什么?** SWIG是一种自动化工具,用于构建C/C++库与各种脚本语言之间...

    swig-3.0.7.tar.gz

    SWIG(Simplified Wrapper and Interface Generator)是一款强大的工具,用于将C和C++代码自动绑定到多种脚本语言,如Python、Perl、Tcl、Java、Ruby等。它简化了创建这些语言的接口过程,使得C/C++库可以方便地在...

    swig-3.0.12.tar

    2. **语言支持**:SWIG支持多种编程语言,如Python、Ruby、Perl、Tcl、Java、JavaScript、C#、Lua等,使得跨语言开发变得更加便捷。 3. **类型转换**:SWIG能自动处理不同语言之间的类型转换,如C/C++的指针、...

    swig-2.0.7.tar.gz

    SWIG是一个强大的工具,主要用于创建程序接口,它可以将C和C++代码与多种高级语言如Python、Perl、Java、Ruby、Tcl、PHP、JavaScript等相连接。它的主要用途是简化创建这些脚本语言的绑定,使得C或C++库可以轻松地在...

    SWIG v3.0.6.zip

    它使得开发者能够轻松地将C或C++编写的库与多种高级编程语言,如Python、Java、Perl、Ruby等进行集成。SWIG的工作原理是通过解析C或C++头文件,自动生成必要的接口代码,从而让这些低级语言的库能够在高级语言环境中...

    swig-2.0.12.tar.gz

    SWIG(Simplified Wrapper and Interface Generator)是一款强大的工具,用于将C和C++代码与多种高级语言(如Python、Perl、Java、Ruby等)接口。在这个案例中,我们讨论的是SWIG的2.0.12版本,它是一个用于安装...

    swig-2.0.6.tar.gz_swig-2.0.1.tar.gz

    7. **多语言支持**:SWIG支持多种脚本语言,如Python、Perl、Ruby、PHP、JavaScript等,使得开发者能够轻松地将C/C++库集成到他们所选择的脚本环境中。 8. **模块和命名空间**:SWIG能够将C/C++的命名空间和模块...

    swigwin-3.0.10.zip

    5. **扩展语言支持**:除了常见的Python、Java等,SWIG还支持许多其他语言,如Ruby、PHP、Lua等。这意味着你可以用SWIG将C/C++库轻松地引入到各种不同的软件开发环境中。 6. **文档生成**:SWIG可以与Doxygen等文档...

Global site tag (gtag.js) - Google Analytics