I’m trying to mass rename all histograms in the working directory after they had been created, but I am running into problems.
Here’s what I do:
int rename() {
TH1D* hist1 = new TH1D("hist1","",10,0,10);
TH1D* hist2 = new TH1D("hist2","",10,0,10);
TH1D* hist3 = new TH1D("hist3","",10,0,10);
cout << "addresses are " << hist1 << " , " << hist2 << " , " << hist3 << endl;
TList* histList = gDirectory->GetList();
Int_t iHist = 0; while(histList->At(iHist)) {
cout << "iHist: " << iHist << " currently points at " << histList->At(iHist) << endl;
TNamed* named = (TNamed*)histList->At(iHist);
TString histName(named->GetName());
if (histName.BeginsWith("hist")) {
histName = "prefix_" + histName;
named->SetName(histName);
cout << "iHist: " << iHist << " now points at " << histList->At(iHist) << endl; }
iHist++;
}
gDirectory->ls();
}
But it doesn’t work. Somehow, changing the name of the first TNamed suddenly makes the TList::At(iHist) point at the second object, so then when you increment iHist for the second iteration of the loop, you work with the third histogram instead of the second. Here is the output so that you can see for yourself:
addresses are 0x3a04f70 , 0x3a05d00 , 0x3a06560
iHist: 0 currently points at 0x3a04f70
iHist: 0 now points at 0x3a05d00
iHist: 1 currently points at 0x3a06560
iHist: 1 now points at 0x3a04f70
iHist: 2 currently points at 0x3a06560
OBJ: TH1D hist2 : 0 at: 0x3a05d00
OBJ: TH1D prefix_hist1 : 0 at: 0x3a04f70
OBJ: TH1D prefix_hist3 : 0 at: 0x3a06560
Is this intended behavior? If yes, does anyone have any idea how can I work around this? Or achieve the same thing using perhaps an altogether different method?
Thanks for your answer. Do I understand correctly that
since TNamed::SetName is virtual , the TH1::SetName is used instead and
since TH1::SetName in its definition removes the old object from the gDirectory and appends the new one at the end, this makes the ->At(0) point at the second object instead etc. ?
Thank you also for your suggestions, both of them work.
The first one feels unsafe, particularly because if I’ll later want to also rename other types whose ::SetName doesn’t suffer from this problem (e.g. TGraph), I’ll run into problems.
The second suggestion should work either way so I’ll go with it.
Thank you!
P.S. If anybody has any ideas for other, perhaps less janky, mechanisms of mass renaming objects, please let me know!
Thanks, using a TIter works in the sense that it renames all objects successfully. However, instead of 3 iterations, the loops goes over 6 (the renaming still appends the object to the end of the list and thus the iterator goes over it twice).
This can be worked around though by using some new conditions such as counting the iterations…
Note that the version that uses At actually also iterates more than once: every At calls starts an iteration from the beginning of the list until the desired element, so you are looking at 1 element for At(0), then 2 for At(1), then 3 for At(2)…
I’d suggest to just copy gDirectory->GetList() in a new TList that does not change while you are iterating.
I guess TH1's are automatically registered in gDirectory while TGraph's are not.
So when you change name to a TH1 it gets automatically re-registered in the gDirectory with the new name.
In case you don’t need the renamed histograms to be registered in the gDirectory, you can de-register them manually just after creation with hist->SetDirectory(nullptr). Which would also solve the original issue.