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

Ando Noriaki n-ando @ aist.go.jp
2010年 5月 24日 (月) 15:01:09 JST


筑波大 木村様

産総研 安藤です

先ほど送ったコードで、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
>



-- 
安藤慶昭@独立行政法人産業技術総合研究所 知能システム研究部門
    統合知能研究グループ 主任研究員, 博士(工学)
    〒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



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