Ruby を組み込みまくれ!

Ruby は強力で便利なインタプリタ言語です。ちょっと調べてみたところ、Ruby (CRuby) は既存の C プログラムに簡単に組み込めることが分かりました。

まずは基本形 (?) です。CRuby の API リファレンスはこれです:http://docs.ruby-lang.org/ja/2.1.0/function/index.html

ちなみに、CRuby を configure する時に –enable-shared というオプションを付けると libruby.so を作ってくれます。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "ruby.h"

void sigint_handler(int sig)
{
  ruby_finalize();
  printf("bye!\n");
  exit(0);
}

int main()
{
  ruby_init();
  /* ちゃんと SIGINT に適当なハンドラをセットしておかないと、^C した途端
   * ruby インタプリタが Segmentation Fault を起こしてフリーズします
   * (KILL シグナルを送れば終了します)
   */
  signal(SIGINT, sigint_handler);

  for (int i = 0; i < 100; i++) {
    /* Kernel.puts i を実行する
     * ruby の「関数」は Kernel モジュールのメソッド
     */
    rb_funcall(rb_mKernel, rb_to_id(rb_str_new2("puts")), 1, INT2FIX(i));
    sleep(1);
  }

  ruby_finalize();
  return 0;
}

※ 2014-05-20
初期化と終了処理はこう書く模様?あと、ID は rb_to_id(rb_str_new2(“foo”)) と書くより rb_intern(“foo”) の方がどう見てもスマート!(詳しくは Extending Ruby 1.9 参照)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "ruby.h"

void sigint_handler(int sig)
{
  printf("bye!\n");
  exit(ruby_cleanup(0));
}

int main(int argc, char *argv[])
{
  ruby_sysinit(&argc, &argv);
  RUBY_INIT_STACK
  ruby_init();
  ruby_init_loadpath();

  /* ちゃんと SIGINT に適当なハンドラをセットしておかないと、^C した途端
   * ruby インタプリタが Segmentation Fault を起こしてフリーズします
   * (KILL シグナルを送れば終了します)
   */
  signal(SIGINT, sigint_handler);

  for (int i = 0; i < 100; i++) {
    /* Kernel.puts i を実行する
     * ruby の「関数」は Kernel モジュールのメソッド
     */
    rb_funcall(rb_mKernel, rb_intern("puts"), 1, INT2FIX(i));
    sleep(1);
  }

  return ruby_cleanup(0);
}

もうちょっと ruby 成分を増やして、for ループを ruby の Fixnum#times メソッドに変えてみましょう:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "ruby.h"

void sigint_handler(int sig)
{
  ruby_finalize();
  printf("bye!\n");
  exit(0);
}

/* proc の中身。yieldarg に yield された値が入る
 * 引数は必要なければ省略可 (rb_funcall の引数を変更し忘れないように)
 *   i.e. times_block(VALUE yieldarg) or times_block()
 */
VALUE times_block(VALUE yieldarg, VALUE foo)
{
  VALUE rv = rb_funcall(rb_mKernel,
      rb_to_id(rb_str_new2("puts")),
      2,
      foo,
      yieldarg);
  sleep(1);

  return rv;
}

int main()
{
  ruby_init();
  signal(SIGINT, sigint_handler);

  /* 100.times{|i| times_block.(i) } を実行する
   */
  rb_funcall_with_block(INT2FIX(100),
      rb_to_id(rb_str_new2("times")),
      0,
      NULL,
      rb_proc_new(times_block, rb_str_new2("foo!")));

  ruby_finalize();
  return 0;
}

ruby.h とかも眺めながら、何度か試してやっとこさできました…。

最近 mruby なんていう組込み機器向けの ruby も出たので、そちらも組み込みまくりましょう。CRuby と似たような感じです。こっちも mruby.h を見ながら頑張りましょう。なお、mruby は SIGINT のハンドラを書かなくても綺麗に終了するみたいです。

#include <stdlib.h>
#include <unistd.h>
#include "mruby.h"

mrb_state *mrb;

void sigint_handler(int sig)
{ 
  mrb_close(mrb);
  printf("bye!\n");
  exit(0);
}

int main()
{
  mrb = mrb_open();
  (void)signal(SIGINT, sigint_handler); 

  for (int i = 0; i < 100; i++) {
    mrb_funcall(mrb, mrb_top_self(mrb), "puts", 1, mrb_fixnum_value(i));
    sleep(1);
  }   
      
  mrb_close(mrb);

  return 0;
}

Ruby のスクリプトファイルを用意しておいて、C からそのファイルで定義したメソッドを呼んだり、ファイルの実行結果を取り出したりとやりたい放題。ちょっとしたツールにも ruby をバンバン組み込んでしまおう!

(コウヅ)

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中