[openrtm-users 01242] Re: サービスポートにおけるデータの受信について

kimura kimura @ ms.esys.tsukuba.ac.jp
2010年 5月 24日 (月) 21:25:17 JST


産総研 安藤様

筑波大 木村です。

ご教示していただきありがとうございます。
ご指摘して頂いた様にソースの方を書き換えましたところ
無事データのやり取りを行うことができました。
結果、説明して頂いたように、ポインタの領域を確保して
いなかったことが問題だったようです。

また、ソースのご指摘のみならず、メモリ・ポインタ等についても
懇切丁寧にご説明していただき大変勉強になりました。
重ねて御礼申し上げます。



(2010/05/24 15:01), Ando Noriaki wrote:
> 筑波大 木村様
>
> 産総研 安藤です
>
> 先ほど送ったコードで、String_dup とありましたが、string_dupの間違いです。
> 説明するのを忘れてましたが、文字列を構造体のメンバーに格納する際には、
> コピーしてあげる必要がありますので、
>
> retval->comment = CORBA::string_dup("OK");
>
> のようにstring_dupを使用してコピーしてあげます。
>
>
> また、構造体やオブジェクトでは _var型という一種のスマートポインタ
> が自動的に宣言され、利用できます。
>
> 今回のコードも_var型を利用すると以下のように書けます。
>
> RTC::RETURN_ID* ManipulatorCommonInterface_CommonSVC_impl::clearAlarms()
> {
>  RTC::RETURN_ID_var retval = new RTC::RETURN_ID();
>  retval->id = 0;
>  retval->comment = CORBA::String_dup("OK");
>  return retval._retn();
> }
>
> このケースでは、あまりありがたみが分かりにくいのですね。
>
> CORBAに限らずC++では通常、new で領域を確保した構造体やオブジェクトは、
> 必ずどこかで delete する必要があります。
>
> void Hoge_impl::hoge_func()
> {
>  MyObject* obj = new MyObject();
>   // obj を使って何かをする。... (1)
>   delete obj;
> }
>
> この例では、関数の終わりでdeleteを読んで、newした領域を解放しています。
> しかし、最後の delete obj がないと、この関数が呼ばれるたびに
> 領域を確保だけして解放しないため、どんどんメモリを食いつぶしていきます。
> #所謂、メモリリークです。
>
> しかし、delete obj があってもメモリリークするケースがあります。
> それは、(1) の部分で例外が発生した場合です。この場合、delete obj は
> 呼ばれず、リークしてしまいます。また、関数内でreturnが複数ある場合は
> delete を気をつけて配置しなければなりませんし、書き忘れることもあります。
>
> そういうときに利用するのがスマートポインタと呼ばれるものです。
> C++ の標準では STL の auto_ptr などがありますし、boost や C++の
> 新しい仕様 (C++0x)では shared_ptr など色々なスマートポインタを標準で利用可能です。
> #これらはC++一般の話なので詳しくは本やWeb等を参照してください。
>
> CORBAは非常に古い規格で、こうしたスマートポインタが一般的でない
> ころから存在しており、またその他複雑な事情で、独自にスマートポインタを
> 持っています。それが_var 型と呼ばれるものです。
>
> _var 型は以下のように、delete を明示的に書かなくても、スコープ(関数の一番下の括弧)
> を抜けると、自動的にnewで確保した領域を解放してくれます。
>
> void Hoge_impl::hoge_func()
> {
>  MyObject_var obj = new MyObject();
>   // obj を使って何かをする。... (1)
>   // delete obj; しなくても、スコープを抜けると自動で解放してくれる。
> }
>
> 最初の話に戻りますが、今回の木村さんのclearAlarms関数では、
> return 後に領域を解放されると困ります。(returnしたRETURN_ID構造体は
> CORBAが相手にデータを渡すまで、データを保持しなければならないから。)
> _var はデフォルトでは スコープを抜けると領域を解放しますが、
> CORBAでは、return でデータを返した後は、CORBA (ORB) がデータを
> 相手に送った後で、自動的に開放することになっています。
> _retn() はそういうときに使用する関数で、領域の自動解放を行わないで
> return した先にゆだねる時に呼びます。
>
> RTC::RETURN_ID* ManipulatorCommonInterface_CommonSVC_impl::clearAlarms()
> {
>  RTC::RETURN_ID_var retval = new RTC::RETURN_ID();
>  retval->id = 0;
>  retval->comment = CORBA::String_dup("OK");
>  return retval._retn(); // メモリの解放は return した先にゆだねる
> } // retval の領域は解放されない
>
> 結局は、前のメールにおいて生のポインタに対して new して delete せずに
> return するのと大して変わりはないのですが、第3者がこの関数をみれば
> 関数内でnew しているのに、delete していないのを見て「あれ?」と思い、
> もしバグを探している最中なら、この関数内でdeleteすべきか、あるいは
> return先でちゃんとメモリが解放されているかを調べ回ると思います。
>
> しかし_var 型ポインタを利用することでCORBAのオブジェクトや構造体で
> あることが明確になりますし、_retn() を呼ぶことで、return した先に領域の
> 解放を任せていることが明確になります。また、_var 型には、(IDLの)in/out/inout
> それぞれの型の引数に値を渡すための in(), out(), inout() といったメンバー
> 関数も用意されているので、それぞれのケースにおいて、領域の解放をそれぞれ
> 自分がやるべきなのか、任せるべきなのかを考えなくても、_retn(), in(), out(), inout()
> 関数を呼んでやるだけでよいようになっています。
>
> void Foo::foo_func()
> {
>    Hoge_var hoge0 = munya0->getHoge();
>    munya1->setHoge(hoge0.in()); // IDL で setHoge(in Hoge data) のように宣言されている
>    munya2->processHoge(hoge0.inout()); // IDL で setHoge(in Hoge data)
> のように宣言されている
>
>    Hoge_var hoge1;
>    bar0->getHoge(hoge.out()); // bar0->getHoge() はHogeの領域を確保しそれを hoge1にセットして返す。
> }
>
> CORBAのその他の変数受け渡し規則については、こちらをご覧ください。
> http://www.openrtm.org/OpenRTM-aist/html/CORBA2FE5A489E695B0E58F97E6B8A1E8A68FE58987.html
>
> こちら等も参考になるかと思います。
> http://otndnld.oracle.co.jp/document/products/tuxedo/tux80j/cref/member.htm
>
>
>   
>>     
>>> OpenRTM MLの皆様
>>>
>>> 筑波大学の木村と申します。
>>>
>>> プロバイダ側からコンシューマ側へ構造体ポインタを渡す際に
>>> Segmantation fault となってしまいデータを送ることが
>>> できないのですが、解決方法をご存じの方がいらっしゃいましたら
>>> ご教示お願いいたします。
>>>
>>>
>>> 今年度からubuntu8.04にてOpenaRTM-aist-1.0を利用しており、
>>> 現在、下記のようなidlファイルを用いてサービスポートを
>>> 作成中なのですが、下記のimpl.cppファイルの様な内容を記述し、
>>> プロバイダ側からコンシューマ側へ構造体ポインタを
>>> 渡す際にプロバイダ側でSegmantation fault となってしまいデータを送ることが
>>> できない状況にあります。
>>>
>>> 過去のメーリングリスト [openrtm-users 00795] にて類似の質問に対する
>>> 解決策を見つけたのですが、本件では、下記のidlファイル内容を
>>> 変更することなく用いたいと考えております。
>>>
>>> 勉強不足で、至らない点も多々あるかとは思いますが、ご教示して
>>> 頂けたら幸いです。
>>>
>>> 以下に再現用のテストで用いたidlファイル、およびimpl.cppファイル内の該当
>>> 部を記します。
>>> ---ここから(idlファイル)---
>>> module RTC
>>> {
>>> struct RETURN_ID
>>> {
>>> long id;
>>> string comment;
>>> };
>>> };
>>> interface ManipulatorCommonInterface_Common
>>> {
>>> RTC::RETURN_ID clearAlarms();
>>> };
>>> ---ここまで(idlファイル)---
>>>
>>> ---ここから(impl.cppファイル)---
>>> RTC::RETURN_ID* ManipulatorCommonInterface_CommonSVC_impl::clearAlarms()
>>> {
>>> RTC::RETURN_ID* retval;
>>> retval->id = 0;
>>> retval->comment="OK";
>>> return retval;
>>> }
>>> ---ここまで(impl.cppファイル)---
>>>       
>> RTC::RETURN_ID* ManipulatorCommonInterface_CommonSVC_impl::clearAlarms()
>> {
>>  RTC::RETURN_ID* retval = new RTC::RETURN_ID();
>>  retval->id = 0;
>>  retval->comment = CORBA::String_dup("OK");
>>  return retval;
>> }
>>
>> こうするとどうなりますでしょうか?
>>
>> 解説:
>> ・宣言したRETURN_IDのポインタに、領域を確保せずに使用しようと
>> しているので Segmentation Fault が発生します。ですので、newで
>> 確保する必要があります。
>> ・CORBAの引数の受け渡し規則で、「returnした構造体など複雑なデータ型
>> は、returnした先で自動的に開放される」、というのがあります。
>> したがって、関数内でnewして領域を確保しても、return後にちゃんと
>> 解放されますので、deleteしなくても大丈夫です。
>>
>> あと、好みの問題ですが、RETURN_ID のようにすべて大文字だと、
>> マクロと混同する人もいるので、構造体であれば ReturnID 等のよう
>> な書き方のほうがよいと思います。
>>
>>
>> --
>> 安藤慶昭@独立行政法人産業技術総合研究所 知能システム研究部門
>>    統合知能研究グループ 主任研究員, 博士(工学)
>>    〒305-8568 つくば市梅園1-1-1 中央第2
>>    e-mail: n-ando @ aist.go.jp, web: http://staff.aist.go.jp/n-ando
>>    OpenRTM-aist: http://www.openrtm.org
>>
>> Noriaki Ando, Ph.D.
>>    Senior Research Scientist, RT-Synthesis R.G., ISRI, AIST
>>    AIST Tsukuba Central 2, Tsukuba, Ibaraki 305-8568 JAPAN
>>    e-mail: n-ando @ aist.go.jp, web: http://staff.aist.go.jp/n-ando
>>    OpenRTM-aist: http://www.openrtm.org
>>
>>     
>
>
>   


-- 

木村 真也
 筑波大学 システム情報工学研究科
 Manipulation System Lab
 Email: kimura @ ms.esys.tsukuba.ac.jp




openrtm-users メーリングリストの案内