Storing pointers to objects in TClonesArrays

From: Nick West (n.west1@physics.oxford.ac.uk)
Date: Thu Dec 17 1998 - 18:27:17 MET


Hi Rene,

I have to say I am a fan of TClonesArray and your suggestion:-

> We could may be add a few additional and specialized containers
> "a la TClonesArray". I am thinking to one specially.
> Suppose the frequent case where you have hits/tracks referencing each
> other. Having pointers as data members of the tiny objects may
> induce a big penalty in I/O (4 or 8 bytes required). In case
> you always reference the same list, it may be more clever to store
> only the index inside the list, typically saving a factor 4 or 8
> in storage.

is a good one but reminded me of some unfinished business.  Earlier this year I
reported a problem with pointers to objects held in TClonesArrays, but got
side-track onto something else.  I can still reproduce the problem in that, on
output, all such objects get written twice, and when reinput, the pointer points
to a copy of the object, NOT the one in the TClonesArray.  I think the basic
problem is that, in TClonesArray::Streamer, the way objects get written out is
by:-

    fCont[i]->Streamer(b);

rather than by writing a pointer, so the pointer address is never recorded and
so, when the pointer gets streamed, there is no entry in the map, so it
generates another copy.   I have a trivial example that demonstrates this:-

    MyClass.cxx,MyClass.h  Define MyClass which has a TClonesArray of MyTrack 
                                  MyTrack has a pointer to another MyTrack

    test_write.cxx         Creates a TTree with a MyClass branch
                           Creates a MyClass, prints, fills tree once and 
                           writes tree out.

    rest_read.cxx          Opens the file, reads the tree and the first event
                           Prints it

In test_write I store 2 MyTracks in the MyClass TClonesArray and point each to
the other.  MyClass::Print() asks each MyTrack to print itself:-

> MyClass 0x1403f4f38 contains:
> 
> MyTrack 0x14043ff08 fPx: 1 fPy: 2 fPz: 3
> Related track: 0x14043ffc8
> 
> MyTrack 0x14043ffc8 fPx: 4 fPy: 5 fPz: 6
> Related track: 0x14043ff08

as you see "Related track" of each points to the other.  However, on input in 
test_read they don't:-

> MyClass 0x1404818a8 contains:
> 
> MyTrack 0x140480e88 fPx: 1 fPy: 2 fPz: 3
> Related track: 0x140480ec8
> 
> MyTrack 0x140480f48 fPx: 4 fPy: 5 fPz: 6
> Related track: 0x140480f08

If you, or anyone else, has time to look, here are the files I am using with 
Digital UNIX V4.0D  (Rev. 878) and Root 2.00/12 


>>>>>MyClass_LinkDef.h

#ifdef __CINT__

#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;

#pragma link C++ class MyClass;
#pragma link C++ class MyTrack;

#endif


>>>>>MyClass.h

#ifndef MYCLASS
#define MYCLASS


// Trivial class with TClonesArray and pointer to object it holds.

#include "Rtypes.h"

#include "TClonesArray.h"

class MyClass : public TObject {

private:
  TClonesArray *fMyClonesArray;           //This will hold MyTrack objects

public:
  MyClass();  
  ~MyClass() {;}
  void Build();
  void Print();


   ClassDef(MyClass,1)  //Trivial class
};

class MyTrack : public TObject {

private:
  MyTrack *fRelatedTrack;
  Float_t  fPx;
  Float_t  fPy;
  Float_t  fPz;

public:
   MyTrack(); 
   MyTrack( Float_t px, Float_t py, Float_t pz); 
  ~MyTrack() {;}
  void AddRelatedTrack( MyTrack* t );
  void Print();


   ClassDef(MyTrack,1)  //Trivial track class
};


#endif

>>>>>MyClass.cxx

#include "MyClass.h"
#include <iostream.h>

ClassImp(MyClass)

 static TClonesArray *gfMyClonesArray;           //This is the permanent copy

//______________________________________________________________________________

  MyClass::MyClass()

{

  //  Create permanent TClonesArray if required.

  if ( ! gfMyClonesArray )gfMyClonesArray = new TClonesArray("MyTrack", 2); 
  fMyClonesArray =  gfMyClonesArray; 

}

//______________________________________________________________________________

  void MyClass::Build()

{

  //  Store two MyTracks.

  MyTrack *t1 = new( (*fMyClonesArray)[0] ) MyTrack(1.,2.,3.);
  MyTrack *t2 = new( (*fMyClonesArray)[2] ) MyTrack(4.,5.,6.);
  t1->AddRelatedTrack(t2);
  t2->AddRelatedTrack(t1);

}

//______________________________________________________________________________

  void MyClass::Print()

{

  cout << "MyClass " << this << " contains:" << endl << endl;
  MyTrack *t = 0;
  for ( Int_t tk_num = 0; tk_num < fMyClonesArray->GetEntriesFast(); tk_num++) {
    if ( t = (MyTrack*) fMyClonesArray->At(tk_num) ) t->Print();
  }

}

ClassImp(MyTrack)

//---------------------------------------------------------------

   MyTrack::MyTrack()

{
   fRelatedTrack = 0;
   fPx = 0.;
   fPy = 0.;
   fPz = 0.;
}

//---------------------------------------------------------------

   MyTrack::MyTrack( Float_t px, Float_t py, Float_t pz )

{
   fRelatedTrack = 0;
   fPx = px;
   fPy = py;
   fPz = pz;

}

  void MyTrack::AddRelatedTrack( MyTrack* t ) 

{
  fRelatedTrack = t;
}

//---------------------------------------------------------------

  void MyTrack::Print()

{
  cout << "MyTrack " << this 
       << " fPx: " << fPx << " fPy: " << fPy << " fPz: " << fPz << endl;
  cout << "Related track: " << fRelatedTrack << endl << endl;

}




>>>>>test_write.cxx

#include "TROOT.h"
#include "TFile.h"
#include "TTree.h"

#include "MyClass.h"

// Initialize the ROOT system
TROOT root("test_write","Test of TClonesArray I/O with pointers");


int main(int argc, char **argv)
{

   TFile *output_file  = new TFile( "test.root", "RECREATE");

   MyClass *myclass = new MyClass;

   TTree *tree = new TTree("T","Test of TClonesArray I/O with ponters");
   Int_t split = 0;
   Int_t bsize = 1000;
   tree->Branch("myclass", "MyClass", &myclass, bsize, split);

   myclass->Build();
   myclass->Print();

   tree->Fill();
   output_file->Write();
   output_file->Close();

   return 0;
}


>>>>>test_read.cxx


#include "TROOT.h"
#include "TFile.h"
#include "TTree.h"

#include "MyClass.h"

// Initialize the ROOT system
TROOT root("test_read","Test of TClonesArray I/O with pointers");


int main(int argc, char **argv)
{

   TFile *input_file  = new TFile( "test.root", "READ");

   MyClass *myclass = new MyClass;

   TTree *tree = (TTree*)input_file->Get("T");
   tree->SetBranchAddress("myclass", &myclass);


   tree->GetEvent( 0 );

   myclass->Print();

   input_file->Close();

   return 0;
}



This archive was generated by hypermail 2b29 : Tue Jan 04 2000 - 00:34:41 MET