Ruby(mruby)とC++を連携させようと考えています。
int等、C++のプリミティブ型や関数、クラスを連携させるのは
mrubybindという外部のソースコードを
借りる形で出来たのですが、std::vector等のstlの連携がmrubybindを改造しても
上手く出来ません。
例えば以下のソースコードを実現できません。
Ruby
1#A_DataはCと連携させたクラス 2data=A_Data.new() 3#b_arrayはA_Dataの中にあるstd::vector<int>型の変数 4#b_arrayに添字を添え、中身を表示する事はできる 5print data.b_array[0] 6#data.b_array[0]にbufを代入しているが、何故か代入できない 7buf=99; 8data.b_array[0]=buf;
mrubyのバインドは通常のRubyとcの連携に似ている事は分かったので
おそらく通常のRubyとCの連携においてSTLをバインドする
方法がわかれば、そのままmrubyでも転用できると考えているのですが
全くやり方が分かりません。
上記のソースコードが実現できるようにRubyとSTLをバインドするには
どうすれば良いのでしょうか?mrubyにおいてmrubybindを使わずに
連携させる方法については下記に書かれています
http://qiita.com/cubicdaiya/items/fc0620c7b9629bb85d6f
もし足りない部分があれば質問をお願いします。よろしくお願いします。
追記
ソースコードをaxfcにアップしました。
https://www.axfc.net/u/3835671
改造した部分のみを書くと、こうなります。
C++
1 2 //改造ここから 3 4 //独自クラスでも扱えるように改造 5 template<typename T> 6 struct Type { 7 static T(*SetFunc)(mrb_state* mrb, mrb_value v); 8 static mrb_value(*RetFunc)(mrb_state* mrb, T& p); 9 static const char* TYPE_NAME; 10 static int check(mrb_state* mrb, mrb_value v) { return mrb_class_ptr(v) != nullptr; } 11 static T get(mrb_state* mrb, mrb_value v) 12 { 13 return SetFunc(mrb, v); 14 } 15 16 static mrb_value ret(mrb_state* mrb, T& p) 17 { 18 return RetFunc(mrb, p); 19 } 20 }; 21 22 template<typename T> 23 T(*Type<T>::SetFunc)(mrb_state* mrb, mrb_value v); 24 25 template<typename T> 26 mrb_value(*Type<T>::RetFunc)(mrb_state* mrb, T& p); 27 28 template<typename T> 29 const char* Type<T>::TYPE_NAME; 30 31 32 //Vectorを扱えるように改造 33 template<typename T> 34 struct Type<std::vector<T>> { 35 static const char* TYPE_NAME; 36 static int check(mrb_state* mrb, mrb_value v) { return mrb_array_p(v); } 37 static std::vector<T> get(mrb_state* mrb, mrb_value v) 38 { 39 std::vector<T> indexes; 40 int len_i = RARRAY_LEN(v); 41 indexes.resize(len_i); 42 for (int i = 0; i < len_i; ++i) 43 { 44 indexes.push_back(static_cast<T>(Type<T>::get(mrb, v))); 45 } 46 return indexes; 47 } 48 static mrb_value ret(mrb_state* mrb, std::vector<T> p) 49 { 50 mrb_value foo = mrb_ary_new(mrb); 51 for (int i = 0; i < p.size(); ++i) 52 { 53 mrb_ary_push(mrb, foo, Type<T>::ret(mrb, p[i])); 54 } 55 return foo; 56 } 57 }; 58 template<typename T> 59 const char* Type<std::vector<T>>::TYPE_NAME = "Array"; 60 //Vector 61 template<typename T> 62 struct Type<std::vector<T>&> { 63 static const char* TYPE_NAME; 64 static int check(mrb_state* mrb, mrb_value v) { return mrb_array_p(v); } 65 static std::vector<T> get(mrb_state* mrb, mrb_value v) 66 { 67 std::vector<T> indexes; 68 int len_i = RARRAY_LEN(v); 69 indexes.resize(len_i); 70 for (int i = 0; i < len_i; ++i) 71 { 72 indexes.push_back(static_cast<T>(Type<T>::get(mrb, v))); 73 } 74 return indexes; 75 } 76 static mrb_value ret(mrb_state* mrb, std::vector<T> p) 77 { 78 mrb_value foo = mrb_ary_new(mrb); 79 for (int i = 0; i < p.size(); ++i) 80 { 81 mrb_ary_push(mrb, foo, Type<T>::ret(mrb, p[i])); 82 } 83 //return mrb_ary_value(&p); 84 return foo; 85 } 86 }; 87 template<typename T> 88 const char* Type<std::vector<T>&>::TYPE_NAME = "Array"; 89 90 //改造ここまで 91 92//中略 93 94 95 //改造ここから 96 template <class T> 97 void bind_set_func(T(*SetFunc)(mrb_state* mrb, mrb_value v)) { 98 Type<T>::SetFunc = SetFunc; 99 } 100 template <class T> 101 void bind_ret_func(mrb_value(*RetFunc)(mrb_state* mrb, T& p)) { 102 Type<T>::RetFunc = RetFunc; 103 } 104コード
問題点はRubyの部分に書いたとおりで、C++で作ったクラスを用いて
作った変数の中にあるVectorの配列の一要素を代入できないという事です。
もし、他に問題点や聞きたい事があれば指摘をお願いします。
二回目の追記
想定通りに動かないRubyのソースを書きました。このソースを通すと「0,99」と表示されるはずが「0,0」と表示されてしまいます。
Ruby
1 2#A_DataはCと連携させたクラス 3data=A_Data.new() 4#b_arrayはA_Dataの中にあるstd::vector<int>型の変数 5#b_arrayに添字を添え、中身を表示する事はできる 6p data.b_array[0] 7#data.b_array[0]にbufを代入しているが、何故か代入できない 8buf=99; 9data.b_array[0]=buf; 10p data.b_array[0]
上のRubyのA_DataはC++で作成した独自クラスをRubyにバインドした物です。
下のような形でバインドせずに通すとエラーが起きるのでご注意下さい。
C++
1 2#include <vector> 3#include "mrubybind.h" 4 5class AData 6{ 7 protected: 8 std::vector<int> b_array; 9 public: 10 AData() 11 { 12 b_array.resize(1); 13 b_array[0] = 0; 14 }; 15 std::vector<int>& GetBArray() 16 { 17 return b_array; 18 } 19 void SetBArray(std::vector<int> set) 20 { 21 b_array=set; 22 } 23 virtual ~AData(){}; 24}; 25 26 27AData* newAData() 28{ 29 return new AData(); 30} 31 32void installADataClass(mrb_state* mrb) 33{ 34 mrubybind::MrubyBind b(mrb); 35 mrubybind::Type<AData>::TYPE_NAME = "A_Data"; 36 b.bind_class("A_Data", newAData); 37 b.bind_instance_method("A_Data", "b_array", &AData::GetBArray); 38 b.bind_instance_method("A_Data", "b_array=", &AData::SetBArray); 39} 40 41int main(int argc, char *argv[]) 42{ 43 // ファイルを開く 44 const char* fn = "hoge.rb"; 45 std::shared_ptr< FILE > file(fopen(fn, "r"), [](FILE* f) { if (f != nullptr) { fclose(f); } }); 46 47 // エラー処理は程々に 48 if (file == nullptr) { 49 std::cout << "Load " << fn << " failed." << std::endl; 50 return 1; 51 } 52 // 仮想マシンを初期化して 53 std::shared_ptr< mrb_state > mrb(mrb_open(), [](mrb_state* p) { if (p != nullptr) { mrb_close(p); } }); 54 // 一時オブジェクトがつくられるのでGC arenaを保存 55 int ai = mrb_gc_arena_save(mrb.get()); 56 mrbc_context *context = mrbc_context_new(mrb.get()); 57 installADataClass(mrb.get()); 58 59 // 実行する ("Hello, World!" が出る) 60 mrb_load_file(mrb.get(), file.get()); 61 if (mrb->exc) { 62 mrb_value exc = mrb_obj_value(mrb->exc); 63 64 // エラー内容を取得 65 mrb_value backtrace = mrb_get_backtrace(mrb.get()); 66 puts(mrb_str_to_cstr(mrb.get(), mrb_inspect(mrb.get(), backtrace))); 67 68 // バックトレースを取得 69 mrb_value inspect = mrb_inspect(mrb.get(), exc); 70 puts(mrb_str_to_cstr(mrb.get(), inspect)); 71 72 // 例外をクリア 73 mrb->exc = 0; 74 } 75 76 mrb_gc_arena_restore(mrb.get(), ai); 77 78 mrbc_context_free(mrb.get(), context); 79 return 0; 80}