ROOTでバグ?

ROOTをつかっていろいろやっているのだけれど、よくわからない現象がおきたのでメモ。

最近になってc++11や14に少しづつ馴染んできている。他人が残していったコードを整理しながらすすめているとこれまで知らなかった利点なども見えてくるわけで、自分なりに工夫しながらつかってみようかしら、と試してきた。

今回の問題は std::shared_ptr 。shared pointer 自体に問題があるわけでもなく、適当なメソッドで std::shared_ptr< TGraph > オブジェクトを返すようにしておいて処理をすすめておけば、delete の問題を自然に回避できて良いですね、ということで使う訳。

今回のケースは、返り値として得たstd::shared_ptr<TGraph>をつかって作図したいという話。作図するのだけど、ROOTの良いところというか面倒な所は、作図はCanvas上のイメージを更新するだけではないというところ。Canvas 上には作図対象のオブジェクトが連結されていて、そのオブジェクトに変更が加えられると自動的に図も更新される。あるデーターを持つグラフオブジェクトがあって、その内容をキャンバス上にグラフ化しましょう、と例えば TGraph::Draw() メソッドを呼ぶ場合、内部的にはTGraphオブジェクトを連結、そのオブジェクトを利用して更新がおこなわれる。オブジェクトに変更を加えると、Canvasに再描写命令を与えなくても勝手に図は更新。

便利なんだけど、寿命をもつオブジェクトを作図するといろいろ問題が。ヒープ領域に new で確保するのじゃなくて、適当なスコープ内でオブジェクトを普通に生成しDrawしても、スコープ終了とともに作図結果もろとも消滅。Canvasは白紙に!回避する手段は、

  • 永続的なオブジェクトとして new する
  • TObject::DrawClone() メソッドを利用して、自身のクローンをCanvasに与えてしまう

後の方法は 元々のオブジェクトに変更を加えてもCanvas内容は更新されなくなり、ROOTのメリットを捨てているようにも感じられます。

さて、今回は std::shared_ptr 。使われなくなると自動的に delete されるもの。TCanvas の方ではそれが shared_ptr なのか知らないわけで、単にDrawをしてもその結果はオブジェクトの寿命とともに海の藻屑と。じゃあ DrawClone でよいかしら、というわけなんだけど・・・・・

普通はこれで上手く行くのだけど、今回はなんだかおかしなことに。Canvasを分割して、複数のプロットを並べて描写していました。分割した領域をPad と呼ぶのですが、PadにはIDがあるので、順に一つづつすすめながら、別の std::shared_ptr< TGraph > をつかって DrawClone。

ところが、結果を見てみると2番目以降のPadでおこなったDrawClone処理は、該当Padに描写するのではなくて、なぜか一つまえのPadに結果を描写しているではないですか。????

なんとなく、DrawCloneした時に、一個前のPadに紐付けされていたObjectと入れ替わってしまう様子。なんじゃこりゃ?ROOTにはクローンを作る TObject::Clone というメソッドがあるので、自分でクローンをつくっておいて、それからDrawしてみるとこれはOK。

TObject::DrawClone のリファレンスマニュアル  ROOT: core/base/src/TObject.cxx Source File を見てみても、結局やっているのは Cloneしてから Draw なわけで、うーんなんだかほんとうによくわからない。

std::shared_ptr< TGraph > をつくっているのは std::make_shared< TGraph > を活用しているので、なにか関係するかもとぐぐってみると

cflat-inc.hatenablog.com

なんていうものもあったり。独自の new、 delete が無視されるというのはもしかしたら問題かしら?なんか昔その関係でいろいろ困った事があったような・・・と思い、 make_shared なしで試してみても問題は解消されない。きっと、 TCanvas 側の方の理由だろうなぁと思うのと、なんとなくいやなんだけど解決策が見つかったのでこれはこれで良しとする。