C++からmruby/cを呼ぶ


先日、九州工業大学の田中先生にmruby/cのステッカーをいただきました。その条件がmruby/cを使うことだったので手元のMacで動かしてみることにします。
しかしただHello Worldを表示させるだけでは付属のSampleコードと変わらないので、C++からmruby/cを呼べないか試してみたいと思います。

環境

Mac OS Catalina 10.15.2

$ cc --version
Apple clang version 11.0.0 (clang-1100.0.33.17)
Target: x86_64-apple-darwin19.2.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

C++プログラムの作成

  1. 本体の入手
    https://github.com/mrubyc/mrubyc 2020年1月末頃のmaster

  2. C++ファイルの作成

少しでも楽をするためにサンプルのコンパイル環境を借りることにします。
mruby/sample_cフォルダ配下に以下のソーコードを書いて下さい。

sample_c/hello.cpp
#include <fstream>
#include <vector>
#include "mrubyc.h"
using namespace std;

#define MEMORY_SIZE (1024*40)
static uint8_t memory_pool[MEMORY_SIZE];

int main(int argc, char *argv[])
{
  ifstream file(argv[1], ios::in | ios::binary);
  vector<char> mrbbuf((istreambuf_iterator<char>(file)), istreambuf_iterator<char>());

  mrbc_init(memory_pool, MEMORY_SIZE);
  mrbc_tcb *task = mrbc_create_task((uint8_t *)&mrbbuf[0], NULL);
  mrbc_start_task(task);
  mrbc_run();
  return 0;
}

内容はファイルからmrubyバイトコードを読み込んで、mruby/cタスクを実行するだけの単純なプログラムです。
C++だとファイル読み込みが二行で済むのが今回唯一の見所。

コンパイル

sample_cのMakefileを間借りさせてもらいます。

sample_c/Makefile
TARGETS = sample_scheduler sample_include sample_concurrent sample_myclass hello
CFLAGS += -g -I ../src -Wall -Wpointer-arith -lc++
LDFLAGS +=
LIBMRUBYC = ../src/libmrubyc.a

all: $(TARGETS)

sample_scheduler: sample_scheduler.c $(LIBMRUBYC)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ sample_scheduler.c $(LIBMRUBYC)

sample_include: sample_include.c $(LIBMRUBYC)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ sample_include.c $(LIBMRUBYC)

sample_concurrent: sample_concurrent.c $(LIBMRUBYC)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ sample_concurrent.c $(LIBMRUBYC)

sample_myclass: sample_myclass.c $(LIBMRUBYC)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ sample_myclass.c $(LIBMRUBYC)

hello: hello.cpp $(LIBMRUBYC)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ hello.cpp $(LIBMRUBYC)

clean:
    @rm -rf $(TARGETS) *.o *.dSYM *~

三ヶ所追加修正
1. TARGETSにhelloを追加
2. CFLAGSに -lc++を追加
3. hello: hello.cpp ... を追加

あとはmakeするだけ

$ make

mrubyバイトコードの生成

ここでHello Worldを書いて出力と行きたいのですが、残念ながらmruby/cにはバイトコードコンパイラが同梱されていないので、直接Rubyコードを実行することができません。そこでmruby(無印)内のmrbcコンパイラが必要になります。

  1. mruby(無印)のインストール
    幸いMacの場合は brew install mrubyコマンドでインストール出来るようです。

  2. Rubyコードのコンパイル
    なんとかmruby/bin/mrbcが実行できる環境が整ったら準備OK。

hello.rb
puts 'Hello mruby/c'

上記のRubyコードをバイトコードにコンパイルします。

$ mrbc hello.rb

hello.mrbcファイルが生成されました。
このファイルをmrubyc/sample_cフォルダにコピーしておきます。

実行

$ ./hello hello.mrb
Hello ruby/c

結論

C++で書くことでコードはコンパクトになりましたが、生成された実行ファイルは259KBになってしまいました。同じようなCのサンプルが190KBだったので70KB増量です。
スペックに余裕がある環境だったらmruby/cじゃなくてmruby(無印)を使うだろうし。

参考文献

mruby/cを導入してみた(Mac編) 課題11(1つ解決)