*.dllや*.soなど、ダイナミックリンクライブラリを扱うためのライブラリです。
Ruby は痒い所に手が届く便利な言語だと知ってはいましたが、こんなに便利になっているとは思いませんでした。
と、いうわけで。
Fibonacci Number
まず。適当な .so ファイルを用意します。
// libfib.cpp #include <map> namespace { std::map<int, int> memo; } extern "C" { int fibonacci_number(int n) { if(n < 1) { return 0; } else if(n <= 2) { return 1; } else { if(memo.find(n) == memo.end()) { memo[n] = fibonacci_number(n - 1) + fibonacci_number(n - 2); } return memo[n]; } } }
.so ファイルの生成。オプションは環境に合わせて指定してください。私は El Capitan 。
$ g++ --std=c++11 -dynamiclib -o libfib.so libfib.cpp
次に。ライブラリを呼び出すモジュールを作成。extern
に続いて文字列で C 言語の関数の定義をそのまま書けば済んでしまうとか、予想外の簡単さ。
# fibonacci.rb require 'fiddle/import' module Fibonacci extend Fiddle::Importer dlload './libfib.so' extern 'int fibonacci_number(int n)' end
サンプル用意。
# sample.rb require './fibonacci' puts Fibonacci.fibonacci_number(10) puts Fibonacci.fibonacci_number(20) puts Fibonacci.fibonacci_number(30) puts Fibonacci.fibonacci_number(40)
実行。
$ ruby sample.rb 55 6765 832040 102334155
簡単すぎる。
Fibonacci Series
配列を扱うときは Array#pack
, Array#unpack
を使って文字列(バイト列)を経由して扱う模様。
配列を扱う関数を追加。
// $ g++ --std=c++11 -dynamiclib -o libfib.so libfib.cpp #include <map> namespace { std::map<int, int> memo; } extern "C" { int fibonacci_number(int n) { // 省略 } void fibonacci_series(int n, int* s) { for(int i = 1; i <= n; ++i) { s[i - 1] = fibonacci_number(i); } } }
同じ要領で .so ファイルを作成。
定義を追加。
# fibonacci.rb require 'fiddle/import' module Fibonacci extend Fiddle::Importer dlload './libfib.so' extern 'int fibonacci_number(int n)' extern 'void fibonacci_series(int n, int* s)' end
呼び出し。
# sample2.rb require './fibonacci' s = [0].pack('i') * 10 # int 型のサイズのバイト列を 10 並べたものを用意 Fibonacci.fibonacci_series(10, s) # 関数呼び出し puts s.unpack('i' * 10) # int 型の値 10 個に戻す
実行。
$ ruby sample2.rb 1 1 2 3 5 8 13 21 34 55
簡単すぎる。