Problem with reading TChain with non-TObject class

Dear Rooters,
I’m trying to read analysistree (the project page is here GitHub - HeavyIonAnalysis/AnalysisTree: AnalysisTree data format). This contains classes that are not inherited from TObject.
In principle, I’m trying to write a code that reads the class name from the TBranch and connects data to such a branch. Unfortunately, this code sometimes works, sometimes crash e.g. whe I set address of branches in this way code works:

    AnalysisTree::EventHeader* pointer = new AnalysisTree::EventHeader();
    if (fInChain->GetBranch("SimEventHeader.")) fInChain->SetBranchAddress("SimEventHeader.", &pointer);
    AnalysisTree::EventHeader* pointer2 = new AnalysisTree::EventHeader();
    if (fInChain->GetBranch("RecEventHeader.")) fInChain->SetBranchAddress("RecEventHeader.", &pointer2);
    AnalysisTree::Detector<AnalysisTree::Particle>* pointer3 = nullptr;
    if (fInChain->GetBranch("SimParticles.")) fInChain->SetBranchAddress("SimParticles.", &pointer3);
    AnalysisTree::Detector<AnalysisTree::Track>* pointer4 = nullptr;  //new AnalysisTree::Detector<AnalysisTree::Track>;
    fInChain->SetBranchAddress("VtxTracks.", &pointer4);

However this part of code crash:

    for (int i = 0; i < brnames.size(); i++) {//branch loop
      TString strx = classname[i];

      if (tobjects[i]) {
      }
      else {
        if (strx.EqualTo("AnalysisTree::EventHeader")) {
          AnalysisTree::EventHeader* pointer = new AnalysisTree::EventHeader();
          fInChain->SetBranchAddress(brnames[i], &pointer);
          std::cout<<"Connecting "<<pointer->Class_Name()<<" to " <<brnames[i]<<std::endl;
        }
        else if (strx.EqualTo("AnalysisTree::Detector<AnalysisTree::Track>")) {
          AnalysisTree::Detector<AnalysisTree::Track>* pointer = new AnalysisTree::Detector<AnalysisTree::Track>;
          fInChain->SetBranchAddress("VtxTracks.", &pointer);
          std::cout<<"Connecting "<<pointer->Class_Name()<<" to " <<brnames[i]<<std::endl;
        }
        else if (strx.EqualTo("AnalysisTree::Detector<AnalysisTree::Particle>")) {
          AnalysisTree::Detector<AnalysisTree::Particle>* pointer = new AnalysisTree::Detector<AnalysisTree::Particle>;
          fInChain->SetBranchAddress("SimParticles.", &pointer);
          std::cout<<"Connecting "<<pointer->Class_Name()<<" to " <<brnames[i]<<std::endl;
        }
        else {
        //do nothing
        }
      }
    }

What is even stranger the lines above will work in macro mode macro.C (3,6 KB)

ROOT Version: 6.22/08
_Platform:_Linux Mint
Compiler: g++ 9.4.0


SimpleLinkDef.h (188 bajtów)
main.cpp (558 bajtów)
Buggy.cpp (3,5 KB)
Buggy.h (748 bajtów)

Hi,

Did you try to debug your code, e.g. with gdb or valgrind?

Best,
D

This is the ‘expected’ behavior per se.
In the code:

        if (strx.EqualTo("AnalysisTree::EventHeader")) {
          AnalysisTree::EventHeader* pointer = new AnalysisTree::EventHeader();
          fInChain->SetBranchAddress(brnames[i], &pointer);
          std::cout<<"Connecting "<<pointer->Class_Name()<<" to " <<brnames[i]<<std::endl;
        }

SetBranchAddress register the address of the pointer … However this pointer goes out of scope immediately and thus its memory location is reused later (in some case) … consequently fInChain, which still uses that addresses, i.e. uses some arbitrary/random data.

The rule is that the value (address of a pointer) passed to SetBranchAddress needs to stray valid during the whole lifetime of the TTree/TChain or until ResetBranchAddresses is called.

Thank you, I thought thas as long I do not call delete on such pointer the memory is allocated even if the pointer goes out of scope.

I thought thas as long I do not call delete on such pointer the memory is allocated even if the pointer goes out of scope.

That is absolutely correct. However the unusual behavior of SetBranchAddress is that it also retains the address of the pointer variable itself (and only indirectly its value (i.e. the address returned by operator new)). This was the best we could do in the early days of C++ to implement shared ownership. (I.e. sometimes the TTree will update the value of the pointer).

1 Like