はじめに
Cで書いたライブラリをRubyから呼び出す方法を簡単に説明します。
公式を含めいくつか説明するサイトはあるのですが、もっとシンプルに教えてほしかったので……。
参考
https://ruby-doc.org/core-2.5.3/doc/extension_ja_rdoc.html
https://qiita.com/suketa/items/ab6b88093de4a54b3b06
作ったサンプル
さっそく作ってみる
CでRuby用のライブラリを作成する
上に示したサンプルのCのコードのとおり。
Initの関数でクラスを定義し(モジュールの定義もできる)、メソッドの定義とそれに対応する関数を作ります。
メソッドにおいては、公式ドキュメントの冒頭に書いてありますが、RubyとCでは型の使い方が違うので、互いにやり取りする場合は専用の関数を使います。つまりは下記のような感じ。
return rb_str_new("Hello World", 11);
Makefileを作ってmake
Makefileは自分で書かないです。extconf.rbを作って、これで ruby extconf.rb
することで作れます。これもサンプルに入ってます。
(Rubyがインストールされていれば実行できると思いますが、もしかしたらruby-dev
とか必要?)
create_makefile()
で指定するモジュール名(クラス名?)はファイル名の大文字小文字が一致しなくても良いです。なのでCファイル名は小文字でhello.c
、クラス名は型名っぽくHello
とかにしました。
ruby extconf.rb
してMakefileができたらmake
します。
ちなみに、Cファイルを作る前にextconf.rbを書いても、makeできないMakefileができたりします。
実行してみる
せっかくなので使ってみるところまで書きます。
サンプルに書きましたが下記のようにできます。
require "./Hello"
hello = Hello::new
hello.methods
#=> [:say, :instance_variable_set, :instance_variable_defined?, ...
# ^ここに作ったメソッドがありますね。
hello.say
#=> "Hello World"
hello.say.class
#=> String
作ってみた背景
最初はCで作ったプログラムをコマンドとしてRubyから実行すればいいかと思っていたのですが、popenのオーバーヘッドがかなり大きいことを知ったので、共有ライブラリを作ってみることにしました。
単発でpopenする場合は、あまりオーバーヘッドは見えません。ただ、1000回ぐらいpopenで実行するか、もしくはライブラリをロードして実行するかでは大きな差が出ると思います。(実行環境によると思いますが)
下記が実験してみた結果。
Hello WorldするCプログラムをpopenで1000回実行した結果:
user system total real
Case01 0.125000 0.296875 2.625000 ( 11.617396)
今回作ってみたライブラリを1000回実行した結果:
user system total real
Case01 0.000000 0.000000 0.000000 ( 0.000436)
今回作ってみたライブラリを1000*1000回実行した結果:
user system total real
Case01 0.109375 0.015625 0.125000 ( 0.130412)
おわりに
Rubyを使う人間としてはRubyのpopenが激重な原因を探してみてもいいかもしれません……。