質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.03%

Ruby(mruby)とvector等のC++のSTLを連携させるには?

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 858

KureteRubyLua

score 192

Ruby(mruby)とC++を連携させようと考えています。

int等、C++のプリミティブ型や関数、クラスを連携させるのは
mrubybindという外部のソースコードを
借りる形で出来たのですが、std::vector等のstlの連携がmrubybindを改造しても
上手く出来ません。

例えば以下のソースコードを実現できません。

#A_DataはCと連携させたクラス
data=A_Data.new()
#b_arrayはA_Dataの中にあるstd::vector<int>型の変数
#b_arrayに添字を添え、中身を表示する事はできる
print data.b_array[0]
#data.b_array[0]にbufを代入しているが、何故か代入できない
buf=99;
data.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

改造した部分のみを書くと、こうなります。

    //改造ここから

    //独自クラスでも扱えるように改造
    template<typename T>
    struct Type {
        static T(*SetFunc)(mrb_state* mrb, mrb_value v);
        static mrb_value(*RetFunc)(mrb_state* mrb, T& p);
        static const char* TYPE_NAME;
        static int check(mrb_state* mrb, mrb_value v) { return mrb_class_ptr(v) != nullptr; }
        static T get(mrb_state* mrb, mrb_value v)
        {
            return SetFunc(mrb, v);
        }

        static mrb_value ret(mrb_state* mrb, T& p)
        {
            return RetFunc(mrb, p);
        }
    };

    template<typename T>
    T(*Type<T>::SetFunc)(mrb_state* mrb, mrb_value v);

    template<typename T>
    mrb_value(*Type<T>::RetFunc)(mrb_state* mrb, T& p);

    template<typename T>
    const char* Type<T>::TYPE_NAME;


    //Vectorを扱えるように改造
    template<typename T>
    struct Type<std::vector<T>> {
        static const char* TYPE_NAME;
        static int check(mrb_state* mrb, mrb_value v) { return mrb_array_p(v); }
        static std::vector<T> get(mrb_state* mrb, mrb_value v)
        {
            std::vector<T> indexes;
            int len_i = RARRAY_LEN(v);
            indexes.resize(len_i);
            for (int i = 0; i < len_i; ++i)
            {
                indexes.push_back(static_cast<T>(Type<T>::get(mrb, v)));
            }
            return indexes;
        }
        static mrb_value ret(mrb_state* mrb, std::vector<T> p)
        {
            mrb_value foo = mrb_ary_new(mrb);
            for (int i = 0; i < p.size(); ++i)
            {
                mrb_ary_push(mrb, foo, Type<T>::ret(mrb, p[i]));
            }
            return foo;
        }
    };
    template<typename T>
    const char* Type<std::vector<T>>::TYPE_NAME = "Array";
    //Vector
    template<typename T>
    struct Type<std::vector<T>&> {
        static const char* TYPE_NAME;
        static int check(mrb_state* mrb, mrb_value v) { return mrb_array_p(v); }
        static std::vector<T> get(mrb_state* mrb, mrb_value v)
        {
            std::vector<T> indexes;
            int len_i = RARRAY_LEN(v);
            indexes.resize(len_i);
            for (int i = 0; i < len_i; ++i)
            {
                indexes.push_back(static_cast<T>(Type<T>::get(mrb, v)));
            }
            return indexes;
        }
        static mrb_value ret(mrb_state* mrb, std::vector<T> p)
        {
            mrb_value foo = mrb_ary_new(mrb);
            for (int i = 0; i < p.size(); ++i)
            {
                mrb_ary_push(mrb, foo, Type<T>::ret(mrb, p[i]));
            }
            //return mrb_ary_value(&p);
            return foo;
        }
    };
    template<typename T>
    const char* Type<std::vector<T>&>::TYPE_NAME = "Array";

    //改造ここまで

//中略


    //改造ここから
    template <class T>
    void bind_set_func(T(*SetFunc)(mrb_state* mrb, mrb_value v)) {
        Type<T>::SetFunc = SetFunc;
    }
    template <class T>
    void bind_ret_func(mrb_value(*RetFunc)(mrb_state* mrb, T& p)) {
        Type<T>::RetFunc = RetFunc;
    }
コード

問題点はRubyの部分に書いたとおりで、C++で作ったクラスを用いて
作った変数の中にあるVectorの配列の一要素を代入できないという事です。
もし、他に問題点や聞きたい事があれば指摘をお願いします。

二回目の追記

想定通りに動かないRubyのソースを書きました。このソースを通すと「0,99」と表示されるはずが「0,0」と表示されてしまいます。

#A_DataはCと連携させたクラス
data=A_Data.new()
#b_arrayはA_Dataの中にあるstd::vector<int>型の変数
#b_arrayに添字を添え、中身を表示する事はできる
p data.b_array[0]
#data.b_array[0]にbufを代入しているが、何故か代入できない
buf=99;
data.b_array[0]=buf;
p data.b_array[0]


上のRubyのA_DataはC++で作成した独自クラスをRubyにバインドした物です。
下のような形でバインドせずに通すとエラーが起きるのでご注意下さい。

#include <vector>
#include "mrubybind.h"

class AData
{
   protected:
       std::vector<int> b_array;
   public:
       AData() 
       {
           b_array.resize(1);
           b_array[0] = 0;
       };
       std::vector<int>& GetBArray()
       {
           return b_array;
       }
       void SetBArray(std::vector<int> set)
       {
           b_array=set;
       }
       virtual ~AData(){};
};


AData* newAData()
{
    return new AData();
}

void installADataClass(mrb_state* mrb)
{
    mrubybind::MrubyBind b(mrb);
    mrubybind::Type<AData>::TYPE_NAME = "A_Data";
    b.bind_class("A_Data", newAData);
    b.bind_instance_method("A_Data", "b_array", &AData::GetBArray);
    b.bind_instance_method("A_Data", "b_array=", &AData::SetBArray);
}

int main(int argc, char *argv[])
{
    // ファイルを開く
    const char* fn = "hoge.rb";
    std::shared_ptr< FILE > file(fopen(fn, "r"), [](FILE* f) { if (f != nullptr) { fclose(f); } });

    // エラー処理は程々に
    if (file == nullptr) {
        std::cout << "Load " << fn << " failed." << std::endl;
        return 1;
    }
    // 仮想マシンを初期化して
    std::shared_ptr< mrb_state > mrb(mrb_open(), [](mrb_state* p) { if (p != nullptr) { mrb_close(p); } });
    // 一時オブジェクトがつくられるのでGC arenaを保存
    int ai = mrb_gc_arena_save(mrb.get());
    mrbc_context *context = mrbc_context_new(mrb.get());
    installADataClass(mrb.get());

    // 実行する ("Hello, World!" が出る)
    mrb_load_file(mrb.get(), file.get());
    if (mrb->exc) {
        mrb_value exc = mrb_obj_value(mrb->exc);

        // エラー内容を取得
        mrb_value backtrace = mrb_get_backtrace(mrb.get());
        puts(mrb_str_to_cstr(mrb.get(), mrb_inspect(mrb.get(), backtrace)));

        // バックトレースを取得
        mrb_value inspect = mrb_inspect(mrb.get(), exc);
        puts(mrb_str_to_cstr(mrb.get(), inspect));

        // 例外をクリア
        mrb->exc = 0;
    }

    mrb_gc_arena_restore(mrb.get(), ai);

    mrbc_context_free(mrb.get(), context);
    return 0;
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • mattn

    2017/08/17 01:11

    どう改造してどう駄目だったか書かないと回答できません。

    キャンセル

  • KureteRubyLua

    2017/08/18 07:27

    大変申し訳ありません。ソースコードの改造部分を追加しました

    キャンセル

回答 1

0

うまく動いている様です。

diff --git a/codegen/mrubybind_types.1.h b/codegen/mrubybind_types.1.h
index d5abd95..276bee1 100644
--- a/codegen/mrubybind_types.1.h
+++ b/codegen/mrubybind_types.1.h
@@ -110,6 +110,25 @@ struct Type<void*> {
   static mrb_value ret(mrb_state* mrb, void* p) { return mrb_cptr_value(mrb, p); }
 };

+template<typename T>
+struct Type {
+    static T(*SetFunc)(mrb_state* mrb, mrb_value v);
+    static mrb_value(*RetFunc)(mrb_state* mrb, T& p);
+    static const char* TYPE_NAME;
+    static int check(mrb_state* mrb, mrb_value v) { return mrb_class_ptr(v) != nullptr; }
+    static T get(mrb_state* mrb, mrb_value v) { return SetFunc(mrb, v); }
+    static mrb_value ret(mrb_state* mrb, T& p) { return RetFunc(mrb, p); }
+};
+
+template<typename T>
+T(*Type<T>::SetFunc)(mrb_state* mrb, mrb_value v);
+
+template<typename T>
+mrb_value(*Type<T>::RetFunc)(mrb_state* mrb, T& p);
+
+template<typename T>
+const char* Type<T>::TYPE_NAME;
+
 //===========================================================================
 // Binder

diff --git a/mrubybind.h b/mrubybind.h
index 5029f5b..0913400 100644
--- a/mrubybind.h
+++ b/mrubybind.h
@@ -30,12 +30,14 @@
 #include "mruby/class.h"
 #include "mruby/data.h"
 #include "mruby/proc.h"
+#include "mruby/array.h"
 #include "mruby/variable.h"
 //#include "mrubybind_types.h"
 // Describe type conversion between C type value and mruby value.

 #include "mruby/string.h"
 #include <string>
+#include <vector>

 namespace mrubybind {

@@ -45,17 +47,12 @@ namespace mrubybind {
 // Base template class.
 template <class T>
 struct Type {
-  // Type name used for error message.
-  // static const char TYPE_NAME[];
-
-  // Returns whether the given mrb_value can be converted into type T.
-  //static int check(mrb_value v) = 0;
-
-  // Converts mrb_value to type T value.
-  //static T get(mrb_value v) = 0;
-
-  // Converts type T value to mrb_value.
-  //static mrb_value ret(mrb_state*, T i) = 0;
+  static T(*SetFunc)(mrb_state* mrb, mrb_value v);
+  static mrb_value(*RetFunc)(mrb_state* mrb, T& p);
+  static const char* TYPE_NAME;
+  static int check(mrb_state* mrb, mrb_value v) { return mrb_class_ptr(v) != nullptr; }
+  static T get(mrb_state* mrb, mrb_value v) { return SetFunc(mrb, v); }
+  static mrb_value ret(mrb_state* mrb, T& p) { return RetFunc(mrb, p); }
 };

 // Fixnum
@@ -144,6 +141,15 @@ struct Type<void*> {
   static mrb_value ret(mrb_state* mrb, void* p) { return mrb_cptr_value(mrb, p); }
 };

+template<typename T>
+T(*Type<T>::SetFunc)(mrb_state* mrb, mrb_value v);
+
+template<typename T>
+mrb_value(*Type<T>::RetFunc)(mrb_state* mrb, T& p);
+
+template<typename T>
+const char* Type<T>::TYPE_NAME;
+
 //===========================================================================
 // Binder

@@ -1233,6 +1239,68 @@ private:
   int arena_index_;
 };

+
+  template<typename T>
+    struct Type<std::vector<T>> {
+        static const char* TYPE_NAME;
+        static int check(mrb_state* mrb, mrb_value v) { return mrb_array_p(v); }
+        static std::vector<T> get(mrb_state* mrb, mrb_value v)
+        {
+            std::vector<T> indexes;
+            int len_i = RARRAY_LEN(v);
+            indexes.resize(len_i);
+            for (int i = 0; i < len_i; ++i)
+            {
+                indexes.push_back(static_cast<T>(Type<T>::get(mrb, v)));
+            }
+            return indexes;
+        }
+        static mrb_value ret(mrb_state* mrb, std::vector<T> p)
+        {
+            mrb_value foo = mrb_ary_new(mrb);
+            for (int i = 0; i < p.size(); ++i)
+            {
+                mrb_ary_push(mrb, foo, Type<T>::ret(mrb, p[i]));
+            }
+            return foo;
+        }
+    };
+    template<typename T>
+    const char* Type<std::vector<T>>::TYPE_NAME = "Array";
+    //Vector
+    template<typename T>
+    struct Type<std::vector<T>&> {
+        static const char* TYPE_NAME;
+        static int check(mrb_state* mrb, mrb_value v) { return mrb_array_p(v); }
+        static std::vector<T> get(mrb_state* mrb, mrb_value v)
+        {
+            std::vector<T> indexes;
+            int len_i = RARRAY_LEN(v);
+            indexes.resize(len_i);
+            for (int i = 0; i < len_i; ++i)
+            {
+                indexes.push_back(static_cast<T>(Type<T>::get(mrb, v)));
+            }
+            return indexes;
+        }
+        static mrb_value ret(mrb_state* mrb, std::vector<T> p)
+        {
+            mrb_value foo = mrb_ary_new(mrb);
+            for (int i = 0; i < p.size(); ++i)
+            {
+                mrb_ary_push(mrb, foo, Type<T>::ret(mrb, p[i]));
+            }
+            //return mrb_ary_value(&p);
+            return foo;
+        }
+    };
+    template<typename T>
+    const char* Type<std::vector<T>&>::TYPE_NAME = "Array";
+
+    template <class T>
+    void bind_set_func(T(*SetFunc)(mrb_state* mrb, mrb_value v)) { Type<T>::SetFunc = SetFunc; }
+    template <class T>
+    void bind_ret_func(mrb_value(*RetFunc)(mrb_state* mrb, T& p)) { Type<T>::RetFunc = RetFunc; }
 }  // namespace mrubybind

 #endif
diff --git a/test/Makefile b/test/Makefile
index 472a399..cbe5454 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -36,6 +36,9 @@ float:    float.o $(MRUBYBIND_OBJ)
 string:    string.o $(MRUBYBIND_OBJ)
     g++ -o $@ $^ $(LIB)

+vector:    vector.o $(MRUBYBIND_OBJ)
+    g++ -o $@ $^ $(LIB)
+
 cptr:    cptr.o $(MRUBYBIND_OBJ)
     g++ -o $@ $^ $(LIB)

テストコードは以下。

#include <mruby.h>
#include <mruby/compile.h>
#include "mrubybind.h"
#include <stdlib.h>

#include <string>
using namespace std;

string emphasize(const char* str) {
  return "* " + string(str) + " *";
}

int main() {
  mrb_state* mrb = mrb_open();

  {
    mrubybind::MrubyBind b(mrb);
    b.bind("emphasize", emphasize);
  }
  if (mrb->gc.arena_idx != 0) {
    fprintf(stderr, "Arena increased!\n");
    return EXIT_FAILURE;
  }

  mrb_load_string(mrb,
      "data=Array.new();"
      "data[0] = 1;"
      "data[1] = 2;"
      "puts data[0] + data[1]");
  if (mrb->exc) {
    mrb_p(mrb, mrb_obj_value(mrb->exc));
    return EXIT_FAILURE;
  }

  mrb_close(mrb);
  return EXIT_SUCCESS;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/18 22:10

    アップしたhoge.rbのコードを動かしてみて下さい。本来0,99と表示されるところが0,0と表示されてしまいます

    キャンセル

  • 2017/08/18 22:53

    んー。ダウンロードできませんでした。質問文に貼って貰えますか?

    キャンセル

  • 2017/08/19 12:39

    貼りました。何度も申し訳ありません

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.03%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる