Adding dynamic array to TTree::GetUserInfo()

Dear ROOTers,

I have a class XDataTreeInfo:

class XDataTreeInfo: public XTreeInfo {
   protected:
      Int_t      fNQuantiles;   //number of quantiles
      Double_t  *fQuantiles;    //[fNQuantiles] Array of quantile values
      Double_t  *fIntenQuant;   //[fNQuantiles] Array of intensity quantiles
   public :
      XDataTreeInfo();
      XDataTreeInfo(const char *name, const char *title);
      virtual ~XDataTreeInfo();
      virtual void AddUserInfo(Int_t nquant, Double_t *q, Double_t *quant);
      ClassDef(XDataTreeInfo,2)
};

As you see this class contains two dynamic arrays.

I am adding this class to my trees using:

void XGeneChipHyb::AddDataTreeInfo(TTree *tree, const char *name, Int_t nquant, Double_t *q, Double_t *quant)
{
   XDataTreeInfo *info = new XDataTreeInfo(name, "");
   info->AddUserInfo(nquant, q, quant);
   tree->GetUserInfo()->Add(info);
}//AddDataTreeInfo

Then I am writing the trees to a root file in method ReadData().

It is also possible to export the user info using method ExportDataTreeInfo().

The relevant code is shown here:

class XDataTreeInfo: public XTreeInfo {

   protected:
      Int_t      fNQuantiles;   //number of quantiles
      Double_t  *fQuantiles;    //[fNQuantiles] Array of quantile values
      Double_t  *fIntenQuant;   //[fNQuantiles] Array of intensity quantiles

   public :
      XDataTreeInfo();
      XDataTreeInfo(const char *name, const char *title);
      virtual ~XDataTreeInfo();

      virtual void AddUserInfo(Int_t nquant, Double_t *q, Double_t *quant);

      ClassDef(XDataTreeInfo,2) //DataTreeInfo
};

//______________________________________________________________________________
XDataTreeInfo::XDataTreeInfo() 
              :XTreeInfo()
{
   fNQuantiles = 0;
   fQuantiles  = 0;
   fIntenQuant = 0;
//??   fQuantiles  = new Double_t[fNQuantiles];  //since default constructor called for permanent obj??
//??   fIntenQuant = new Double_t[fNQuantiles];  //since default constructor called for permanent obj??
}//Constructor

//______________________________________________________________________________
XDataTreeInfo::XDataTreeInfo(const char *name, const char *title) 
              :XTreeInfo(name, title)
{
   fNQuantiles = 7;
   fQuantiles  = new Double_t[fNQuantiles];
   fIntenQuant = new Double_t[fNQuantiles];
}//Constructor

//______________________________________________________________________________
XDataTreeInfo::~XDataTreeInfo()
{
   if (fIntenQuant) {delete [] fIntenQuant; fIntenQuant = 0;}
   if (fQuantiles)  {delete [] fQuantiles;  fQuantiles  = 0;}
}//Destructor

//______________________________________________________________________________
void XDataTreeInfo::AddUserInfo(Int_t nquant, Double_t *q, Double_t *quant)
{
   if (nquant > fNQuantiles) {
      if (fIntenQuant) {delete [] fIntenQuant; fIntenQuant = 0;}
      if (fQuantiles)  {delete [] fQuantiles;  fQuantiles  = 0;}

      fQuantiles  = new Double_t[nquant];
      fIntenQuant = new Double_t[nquant];
   }//if

   fNQuantiles = nquant;

   memcpy(fQuantiles,  q,     nquant*sizeof(Double_t));
   memcpy(fIntenQuant, quant, nquant*sizeof(Double_t));
}//AddUserInfo


//______________________________________________________________________________
//______________________________________________________________________________
void XGeneChipHyb::AddDataTreeInfo(TTree *tree, const char *name, 
                   Int_t nquant, Double_t *q, Double_t *quant)
{
   XDataTreeInfo *info = new XDataTreeInfo(name, "");

   info->AddUserInfo(nquant, q, quant);

   tree->GetUserInfo()->Add(info);
}//AddDataTreeInfo

//______________________________________________________________________________
Int_t XGeneChipHyb::ReadData(ifstream &input, Option_t *option, const char * /*sep*/,
                    char delim, Int_t split)
{
   char     nextline[kBufSize];
   Int_t    i, x, y;
   Double_t inten, stdev;

   Int_t     nquant = 7;
   Double_t  q[]    = {0.0, 0.1, 0.25, 0.5, 0.75, 0.9, 1.0};
   Double_t *quantI = 0; 
   if (!(quantI = new (nothrow) Double_t[nquant])) return errInitMemory;

// Create data tree
   TString exten = Path2Name(option, ".", "");
   fDataTreeName = fTreeName + "." + exten;
   TTree   *datatree = new TTree(fDataTreeName, fSchemeName);
   if (datatree == 0) return errCreateTree;

   XGCCell *cell    = new XGCCell();
   Int_t    bufsize = XManager::GetBufSize();
   datatree->Branch("DataBranch", "XGCCell", &cell, bufsize, split);

// Read header line containing column names
   input.getline(nextline, kBufSize, delim);
   if (strncmp("CellHeader=", nextline, 11) != 0) return errMissingLine;

// Read data and store in data tree
   for (i=0; i<fNCells; i++) {
      input.getline(nextline, kBufSize, delim);
      if (!input.good()) {err = errPrematureEOF; break;}
      sscanf(nextline,"%i %i %lf %lf %hi", &x, &y, &inten, &stdev);

      // fill data tree
      cell->SetX(x);
      cell->SetY(y);
      cell->SetIntensity(inten);
      cell->SetStdev(stdev);
      datatree->Fill(); 
   }//for_i

// Write data tree to file 
   AddDataTreeInfo(datatree, datatree->GetName(), nquant, q, quantI);
   WriteTree(datatree, TObject::kOverwrite);

// Delete data tree from RAM
   datatree->Delete("");
   datatree = 0;
   delete cell;

   if (quantI) {delete [] quantI; quantI = 0;}

   return errNoErr;
}//ReadData

//______________________________________________________________________________
Int_t XGeneChipHyb::ExportDataTreeInfo(Int_t n, TString *names, 
                    ofstream &output, const char *sep)
{
// Get trees
   TTree         **tree = new TTree*[n];
   XDataTreeInfo **info = new XDataTreeInfo*[n];
   if (fTrees->GetSize() == 0) {
   // Get trees from names
      for (Int_t k=0; k<n; k++) {
         tree[k] = (TTree*)gDirectory->Get((names[k]).Data());
         if (!tree[k]) return errGetTree;

         info[k] = (XDataTreeInfo*)tree[k]->GetUserInfo()->At(0);
      }//for_k
   }//if

// Output header
   output << "Parameter";
   for (Int_t k=0; k<n; k++) output << sep << names[k].Data();
   output << endl;

// Output parameters
   output << "NQuantiles";
   for (Int_t k=0; k<n; k++) output << sep << (Int_t)(info[k]->GetValue("fNQuantiles"));
   output << endl;

   Double_t **quant = new Double_t*[n];
   Double_t **inten = new Double_t*[n];
   for (Int_t k=0; k<n; k++) {
      quant[k] = info[k]->GetQuantiles();
      inten[k] = info[k]->GetIntenQuantiles();
   }//for_k

   Int_t nq  = (Int_t)(info[0]->GetValue("fNQuantiles"));
   for (Int_t i=0; i<nq; i++) {
      TString str; str.Form("Intensity_Q%4.2f", quant[0][i]);

      output << str.Data();
      for (Int_t k=0; k<n; k++) output << sep << inten[k][i];
      output << endl;
   }//for_i

   delete [] inten;
   delete [] quant;

   for (Int_t k=0; k<n; k++) {
//no!      SafeDelete(info[k]);
      SafeDelete(tree[k]);
   }//for_k

   delete [] info;
   delete [] tree;

   return errNoErr;
}//ExportDataTreeInfo

Although I have run my program using valgrind and did not find any problem, I want to make sure
that everything is ok, thus I have the following questions:

1, Is it ok that I define the default constructor as follows:

XDataTreeInfo::XDataTreeInfo():XTreeInfo()
{
   fNQuantiles = 0;
   fQuantiles  = 0;
   fIntenQuant = 0;
}

or do I need to init the arrays as in the constructor:

XDataTreeInfo::XDataTreeInfo(const char *name, const char *title):XTreeInfo(name, title)
{
   fNQuantiles = 7;
   fQuantiles  = new Double_t[fNQuantiles];
   fIntenQuant = new Double_t[fNQuantiles];
}

The reason I am asking is that when I export the user info from the trees stored in the root
file, then the default contructor is called.

2, In method AddDataTreeInfo() I am creating:

   XDataTreeInfo *info = new XDataTreeInfo(name, "");

but do not delete it. I am not sure if this is ok although the help for TTree:GetUserInfo() says:
“By default the TTree destructor will delete all objects added to this list.”

I would appreciate if you could tell me whether my code is correct or if there are some hidden problems.

Thank you in advance
Best regards
Christian

[quote]1, Is it ok that I define the default constructor as follows:[/quote]Yes, setting them to zero is fine (the Streamer will allocated them when reading if needed).

[quote]but do not delete it. I am not sure if this is ok although the help for TTree:GetUserInfo() says:
“By default the TTree destructor will delete all objects added to this list.”[/quote]Yes, the documentation is correct, once added to the list of user info, the object is owner by the TTree (and consequently is deleted whenever the TTree is deleted).

Cheers,
Philippe.

Dear Philippe,

Thank you very much for your confirmation.

Best regards
Christian