LoginSignup
1
0

More than 5 years have passed since last update.

mrubyからc経由でgoの関数を呼ぶ

Last updated at Posted at 2017-12-07

go言語はビルド時にbuildmode=c-archiveを指定すると、静的ライブラリを生成することができます。

$ go build -buildmode=c-archive main.go
$ ls
main.a main.go main.h

今回はこれをmrubyに組み込んでみたいと思います。

mruby-mrbgem-template で mrbgemのひな型を作成

以下のコマンドでひな型を作成し、作成したディレクトリに移動します。

$ git clone https://github.com/matsumotory/mruby-mrbgem-template
$ cd mruby-mrbgem-template
$ rake
$ cd mruby-example

go側の実装

golang の Windows 版が buildmode=c-archive をサポートした。 を参考に作成します。

main.go
package main

import (
    "C"
    "fmt"
)

//export PrintLine
func PrintLine(text *C.char) {
    fmt.Println(C.GoString(text))
}

func main() {
}

今回は単純に関数の引数で渡された文字列をfmt.Println()で表示し、文字列をそのまま返す。というだけの関数PrintLineを作成しています。ファイルはmruby-exampleの下にgoというディレクトリを作成し、その中に置きました。

goディレクトリに移動し、以下のようにビルドします。

$ go build -buildmode=c-archive -o libmain.a main.go

mruby(c)側の実装

基本的な構造はmruby-mrbgem-templateが作ってくれているので、ひな型mrbgemの中のsrcディレクトリに移動し、mrb_example.cを編集していきます。

今回は単純にgo側で定義した関数を呼び出して、戻り値で戻ってきた値を返して、mrubyスクリプトとgoの関数をブリッジする関数を足します。

mrb_exmaple.c
static mrb_value mrb_example_hi(mrb_state *mrb, mrb_value self)
{
  return mrb_str_new_cstr(mrb, "hi!!");
}

// 今回はこの部分の関数を定義
static mrb_value mrb_example_hi2(mrb_state *mrb, mrb_value self)
{
  extern char* PrintLine(char*);
  char *ret;
  ret = PrintLine("hi!! from go");
  return mrb_str_new_cstr(mrb, ret);
}

void mrb_mruby_example_gem_init(mrb_state *mrb)
{
  struct RClass *example;
  example = mrb_define_class(mrb, "Example", mrb->object_class);
  mrb_define_method(mrb, example, "initialize", mrb_example_init, MRB_ARGS_REQ(1));
  mrb_define_method(mrb, example, "hello", mrb_example_hello, MRB_ARGS_NONE());
  mrb_define_class_method(mrb, example, "hi", mrb_example_hi, MRB_ARGS_NONE());
  mrb_define_class_method(mrb, example, "hi2", mrb_example_hi2, MRB_ARGS_NONE()); // あと、この部分の忘れずに追加
  DONE;
}

mrbgem.rake の編集

コードはできたので、次にビルドする為の設定を書いていきます。まずはmrbgem.rakeにgoのライブラリをリンクしましょう。私の環境がWindowsなので、余分にライブラリを追加していますが、macならmainのみで大丈夫なはずです。

mrbgem.rake
MRuby::Gem::Specification.new('mruby-example') do |spec|
  spec.license = 'MIT'
  spec.authors = 'your-name'

  # この2行を追加
  spec.linker.libraries << %w(main ws2_32 winmm)
  spec.linker.library_paths << File.join(__dir__, "go")
end

.travis_build_config.rb の編集

macの人は不要かもしれません。私の環境では以下の設定が必要でした。

.travis_build_config.rb
MRuby::Build.new do |conf|
  toolchain :gcc
  conf.gembox 'default'
  conf.gem File.expand_path(File.dirname(__FILE__))
  conf.enable_test

  conf.cc do |cc|
    cc.command = 'x86_64-w64-mingw32-gcc'
  end

  conf.linker do |linker|
    linker.command = 'x86_64-w64-mingw32-gcc'
  end

  conf.archiver do |archiver|
    archiver.command = 'x86_64-w64-mingw32-gcc-ar'
  end
end

ビルド

mruby-mrbgem-templateで作成したひな型mrbgemにはRakefileがついていて、コマンド一発でビルドしてくれます。

$ rake

実行

ビルドが成功すると、./mruby/binの下にmrubyの実行ファイルができています。試しに以下のスクリプトを実行してみましょう。

test.rb
puts Example.hi
puts Example.hi2
$ ./mruby/bin/mruby test.rb
hi!!
hi!! from go
hi!! from go

無事にgo側でfmt.Println()した内容と戻り値をputsした内容の両方が表示されていますね。

まとめ

mrubyを使って、goの関数の呼び出しと戻り値を使ってのmrubyスクリプトの実行を行いました。
go特有の並列処理をうまく組み合わせることができれば、もっと面白いことができるかもしれません。
また、C言語に限らず、mrubyを拡張する一つの方法になるのかもしれませんし、
逆にgo言語をmrubyで拡張していくことができるかもしれませんね。

今後の課題

もし、mrbgemの一部としてgoのソースを含めるのであれば、静的ライブラリのビルドも自動でできるようにしていきたい

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0