Storing pointers to objects in TClonesArrays

Nick West (n.west1@physics.oxford.ac.uk)
Thu, 17 Dec 1998 17:27:17 GMT


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