Ruby Fiddle を引っ掻いて

しのかろ
7 min readFeb 25, 2015

--

RubyにはFiddleという添付ライブラリーがある。これを使用すればダイナミック・リンク・ライブラリーがRubyから利用できるようになる。つまり、コンパイラーで生成されたバイナリー・コードを呼び出すことができるので、Rubyではできなきことや、Rubyでは遅い処理を高速化することができる。

たとえば、RubyからMP3ファイルから音声ファイルを読み取るには? ファイルそのものを開くことはFileクラスを使えばできるが、その中身、特に音声データーを展開するには複雑な数式処理が待ち受けている。処理の方法は調べればわかるが、大量の計算処理を行うにはRubyは向かない。それが、音声ファイルとくれば音楽を聴くのに待たされるのは許容できないだろう。

知ってのとおり、今ではMP3はダブルクリックすれば即座に再生されるものだ。Cなどのコンパイラーから生成されたバイナリー・コードならば、この程度の処理に時間はかからない。そして、そのバイナリー・コードはDLLやSOファイルとして外部からの利用ができる場合がある。

つまり、RubyからDLLやSOをRubyコード的に呼び出せればコンパイラーについての知識がなくても良いことになる。それを可能にするのがFiddleライブラリーだ。

Fiddleの使い方や詳細は公式ドキュメントにあるので、まずそれに目を通す必要がある。しかし、Rubyの知識だけではほとんど理解できないだろう。これは当然のことで、これから呼び出そうとするモノはRubyとはまったく違う考え方で作られたものなのだ。

では、Fiddleを奏でられるように(Fiddleとはバイオリンのことだ)なるには何を知らなければならないのだろうか? 結論はC言語、つまりCのソースを読めること、そしてRubyがどのように動いているかの知識が必要だ。

C言語に関しては、C関数を呼び出す際にポインター、構造体、メモリー管理の仕組みが普通に出現する。特にメモリー管理についてはCのソースを読めなければならない。それには理由がある。

メモリー管理とはmalloc関数で確保されるメモリー領域のことだが、これを開放せずにいるとRubyの利用可能メモリーが使われたままになる。もちろん、作業続行中であれば確保したままでよい。問題は作業を終了したオブジェクトが開放されるときだ。

オブジェクトの開放、という現象はRubyの動作に関する知識だ。Rubyではどこからも参照されていないオブジェクトはGC(ガベージコレクション)によって人知れず開放される。もうひとつ知るべきことがある、オブジェクトの開放処理はGCの仕事であって、オブジェクト自身では自分を開放できない。他の言い方をすればデストラクターを作れない、ということだ。

Rubyの内部ではGCから開放指示された時にオブジェクト自身を解放するコードを用意している。もちろんGCからの開放指示に間違いはない、という前提になっている。ユーザーがRubyをC言語で拡張した場合には、この開放処理関数を用意しておく。この関数の呼び出しを行うのはRubyのGCだ。

さて、話をFiddleを利用してC関数を呼び出す時に戻す。呼び出して作業を行うところまではC言語の知識で行えるが、作業中に確保されたメモリー領域を開放するときが問題だ。開放指示がRubyから行われる所までは分かっている。

ここで、Cのソースを読める必要がでてくる。C関数側が作業に応じてメモリーを確保しているからである。そして、作業中に確保するメモリー領域についてはたいていの場合、ドキュメントには掲載されていない。

一応、手助けは用意されている場合がある。たとえば、作業終了時に呼び出す関数が用意されており、その関数が作業用に確保したメモリーをすべて解放する、といった具合だ。

ただし、その関数が本当にメモリーを全て解放するかはソースを見て確認する以外に方法はない。利用しようとしているダイナミック・リンク・ライブラリーがオープン・ソースであればソースを追っていくことが可能だ。

Fiddleを利用するときのメモリー管理には別のことも気にかける必要がある。C関数の戻り値が結果データーへのポインターを返す場合だ。メモリーを解放した後にこのポインターを参照することはできない。しかし、Fiddleで生成された戻り値オブジェクトはC関数側でメモリー開放を行ってもRubyコード上に存在することができる。

他にはFiddleでC構造体を生成(メモリー確保)した場合だ。これを開放するのは簡単ではない。なぜならばFiddleは通常、メモリー開放の仕組みのないRubyオブジェクトを生成するからだ。これをRuby GC の仕組みに適合するようにRubyで書くには公式ドキュメントの情報だけでは書くことはできない。ただし、方法は用意されているのでオブジェクト開放のタイミングでメモリー開放を行うことは可能だ。もちろん、C関数が作業中にRubyオブジェクトが開放されないようにRubyコードを書く必要がある。

以上、述べたようにFiddleを利用するとRubyらしからぬ複雑な作業が待ち受けている。それでもC言語でコードを書くより生産性が高めることができるといえる。ここでは、メリットをいくつかあげる。

  1. Ruby的な複雑な可変引数を実装できる。
  2. 例外が簡単に記述できる。特に例外メッセージ。そしてエラーへの寛容。
  3. Rubyの柔軟なデータ構造を利用できる、簡単に言えば数値計算、それから文字列処理

Rubyでコードを書くのだからメリットはいくらでもある。一方、Fiddleを利用することのデメリットもある。Fiddle実践中に気づいたことをいくつかあげる。

  1. C構造体の定義が弱い。また記述しづらい。データー型のサポートが弱い。
  2. Cのヘッダーファイルの読み込みができない。FiddleはRubyで動作しているので当然ではあるが、C関数はヘッダーファイルの存在が前提となっている。

Cのヘッダーファイルを人間の目で解読し手で展開することになるのだが、このファイルにはプリプロセッサーを利用して書かれている。自動処理を前提に書かれたコードを人間が読み解くのだから、これは生産性が低い。Fiddleを使いこなしている人はこれを要領良く処理する方法を知っているのかもしれない。残念ながら私はまだ知らない。

この記事の最後に要領よくCのソースを追跡する方法を紹介してこう。Windows7環境であることが前提だが、たぶん同等の機能は他のOSにもある。

まず、ソースコードをDLしてフォルダーに展開しておこう。次にスタートメニューの検索窓に調べたい関数の名前を打ち込み検索を実行する。そうすると、該当するファイルを見つけてくれる。検索に時間がかかるならWindowsがインデックスを作るように薦めてくる。もちろん、インデックスを作成しておこう。

この方法のメリットは開発環境を構築すると複数のフォルダーにバージョン違いのソースが散らばったりする。こういったときにも、この方法が手っ取り早い。この全文検索ではコンパイル済みのファイルに埋め込まれたキーワードも探し出す。

もうひとつの方法はネット上のソースコード公開サイトを利用する方法だ。例としてRubyのソースコードを公開しているThe Ruby Cross Reference を紹介する。ソース中の関数名などのキーワードにリンク用意してありリンクをたどってキーワードの定義場所やコードへ移動できる。

欠点はサイトの反応が遅いことだが、Rubyのソースのようなマクロが多用されているようなコードを読むときにはこちらのほうが向いている。なぜならマクロ名でソースを全文検索すれ、ほぼ全てのファイルが対象になるからだ。

--

--

しのかろ

自分の作業や考えをメモ。Ruby programmer/BATTARI WORKS