<メモブログ>[未解決]shared_ptr"のポインタ"はどう扱えばいいのだ・・・
TL;DR
- C++ と mruby
- shared_ptr"のポインタ"を void* にキャストして保存するときは、vector等のコンテナ要素をポインタにする
- というか、ベストプラクティスがわからん、そもそもこの設計はよくないかも。
問題
C++ と mruby を使ったプログラムを書いている。
C++の世界では、ほぼ全てのオブジェクトが、shared_ptr<T>
で管理されており、例えばライブラリのインターフェイスの引数も const shared_ptr<T>&
になっている。
C++の世界だけで完結するならこれでいいのだけれども、mrubyをバインディングしようとすると困った。
このような、グルー言語系のオブジェクトは void*
で持たせるようになっていて、mrubyだとDATA_PTR
マクロでRubyのオブジェクト型であるmrb_value
型を渡すと、void*
のポインタが返ってくる。
ここにオリジナルの構造体や値のポインタを入れれば良い。
さて、ここで、 C++ のshared_ptr<T>
のオブジェクトをRuby側にもたせようとする方法は2つ思いつく。
- shared_ptr
のポインタ を格納する。 shared_ptr<T>#get()
で生ポに変換して格納する。
なお、いずれの場合もshared_ptr<T>
の参照カウントは無効になる。(これは void* に格納する以上諦めるしかない。)
1.の場合はハマる確率がかなり高い。 色々原因はあるが、まず、 shared_ptr自体のオブジェクトを関数内で生成するタイプならば、その関数を抜けた時点で、格納したオブジェクトは無効になる。 なので、次に使おうとしたときに、アクセス違反になる。(たまに動くけど、それは偶然でコード的にはNG)
void create_object() { shared_ptr<HOGE> sptr = std::make_shared<HOGE>(); DATA_PTR(rb_object) = &sptr } // ここで sptr 自体は無効になる。C言語をやっていれば当たり前のことではあるが。。。
2.場合は格納は問題ないが、次にC++の世界に戻ってきたときに困る。C++のインターフェイスにオブジェクトを渡すときに型が一致しないために、shared_ptr オブジェクトを作ってやる必要がある。
そのときに、参照カウントがすでにある shared_ptr<T>
と矛盾してしまう。
void called_by_ruby() { HOGE *hoge_ptr = static_cast<HOGE*>( DATA_PTR(rb_object) ); const shared_ptr hoge_sptr = shared_ptr<HOGE>( hoge_ptr ); // この shared_ptr の参照カウントはまた 0 になる c_plus_plus_function( hoge_sptr ); //c_plus_plus_function(const shared_ptr<T>& _hoge ); }
解決(?)
静的クラスでも、静的変数でもいいので、std::unorderd_map
か std::vector
等を作り、そのインデックスを指してやる。
今回は、std::unorderd_map
にし、キーを mrb_value
にした。当然ハッシュ関数も必要なので作ってやる。(DATA_PTRのポインタをキーにする)
struct Mruby_data_value_hash { size_t operator()(const mrb_value& m_val) const { return reinterpret_cast<size_t>(DATA_PTR(m_val)); } }; struct Mruby_data_value_equal { bool operator()(const mrb_value& left, const mrb_value& right) const { return DATA_PTR(left) == DATA_PTR(right); } }; std::unorderd_map<mrb_value, std::shared_ptr<HOGE>, Mruby_data_value_hash, Mruby_data_value_equal> sptr; mrb_value mrb_hoge_init(mrb_state* mrb, mrb_value self) { DATA_PTR(self) = (&sptr[self]); // コンテナのインデックス **のアドレス** }
なお、そもそも設計がマズいかもしれない。理屈は Lua等でもおこるはず。いい方法があったら教えてください。