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;
}