VC++とSWIGでRubyの拡張ライブラリを作る
RubyはC/C++を用いて拡張ライブラリを作成することができますが、用意されているAPIはあくまでもCレベルのものであり、C++のクラスをRubyから使えるようにするためにはラッパを作成する必要があります。今回は、ラッパ部分を自動生成してくれるツールであるSWIGを利用して作業を簡略化したいと思います。
以下の内容は、Visual Studio 2008 ProfessionalとActiveScriptRuby 1.8.7(p249)、SWIG 1.3.40の組み合わせで試しています。ActiveScriptRubyとSWIGは以下のサイトからダウンロードできます。なお、ここではActiveScriptRubyとSWIGのディレクトリにパスを通す作業は省略しています。
- COM Meets Ruby (ActiveScriptRubyの配布元)
- Simplified Wrapper and Interface Generator (SWIGの配布元)
下準備
最初に一度だけ必要な設定を行います。
環境変数の設定
Rubyのヘッダファイルを参照できるようにするため、環境変数を設定します。パス中のは各自Rubyをインストールしたディレクトリに置き換えてください。
- RUBY_INCLUDE
<ruby-1.8>\lib\ruby\1.8\i386-mswin32
- RUBY_LIB
<ruby-1.8>\lib
VC++の設定
VC++からRubyのヘッダファイルを参照できるようにするため、オプションのVC++ ディレクトリを開いて以下の項目を追加します。
- インクルード ファイル
<ruby-1.8>\lib\ruby\1.8\i386-mswin32
- ライブラリ ファイル
<ruby-1.8>\lib
config.hを編集
VC++ 2008だとActiveScriptRubyに同梱されているconfig.hのバージョンチェックで弾かれてしまうようなので無効化します。あまりよろしくないことだとは思いますが、動いたのでよしとしておきます。config.hは <ruby-1.8>\lib\ruby\1.8\i386-mswin32
にあります。
動作確認
プロジェクトの作成
Visual Studioでプロジェクトを作成します。構成はWin32 プロジェクト、DLL、空のプロジェクトとします。以下、プロジェクト名はRubyTestに設定したものとして進めます。
プロジェクトにモジュール定義ファイルを追加
プロジェクトにモジュール定義ファイル(.DEF)を追加します。このファイルは外部にエクスポートする関数を指定するために必要です。Rubyは拡張ライブラリのロード時に”Init_ライブラリ名”の関数を呼び出すので、ここではInit_RubyTestをエクスポートするように設定しておきます。Init_RubyTestの定義自体はSWIGが自動的に生成してくれるので特に気にする必要はありません。
リンカの設定
リンク時にRubyのライブラリファイルがリンクされるよう設定します。プロジェクトのプロパティでリンカ→入力と辿って追加の依存ファイルに”msvcrt-ruby18.lib”を指定すればOKです。
テスト用のクラスを作成
適当にクラスを作成します。中身は何でもいいのですが、ここではhello.cpp、hello.hppを作成して以下のように記述しました。
インターフェイスファイルの作成
SWIGがラッパーを作成するために必要なインターフェイスファイルを用意します。hello.cpp、hello.hppと同じディレクトリにhello.iというファイルを作成し、以下のように記述します。
SWIGの実行
コマンドプロンプトで以下のようなコマンドを実行すると、Helloクラスをラップしたhello_wrap.cxxが生成されます。生成されたファイルはプロジェクトに追加しておきます。
ビルド
この状態でビルドを行うと、成功すればRubyTest.dllが生成されるはずです。
Rubyから呼び出してみる
作成したモジュールを呼び出してみます。test.rbを作成し、以下のように記述します。
ruby test.rbで実行してみます。実行する前にtest.rbと同じディレクトリに先ほどビルドしたRubyTest.dllを生成しておきましょう。うまくいっていれば以下のように出力されるはずです。
おわりに
Ruby向けの拡張ライブラリ作成は驚くほど簡単で拍子抜けしました。これなら重い処理はC++、さくっと書きたい部分はRubyのように組み合わせた開発が簡単にできますね。
ただ、今回の方法ではクラスの構成が変わるたびにラッパを生成し直す必要があり非常に不便です。Makefileを使うという手もありますが、現在Visual Studioの設定でうまく自動化できないか試しているところです。うまくいった場合は、次回その方法を説明しようと思います。