Re: Writing an 'array' of numbers into a root file

From: Rene Brun (Rene.Brun@cern.ch)
Date: Fri Nov 13 1998 - 11:35:19 MET


Hi Sergey,
As you have found, the classes TArrayF, etc do not derive from
TObject, BUT they have their Streamer method.
To be streamed to the output buffer, a class does not need to derive
from TObject. Here are the rules:
 -If a class derives from TObject, rootcint can generate automatically
  the Streamer function if ClassDef is specified in the header file.
 -If a class does not derive from TObject (such as TArrayF), you
  can implement the Streamer function (case of TArrayF) and still use
  ClassDef in the header file.
 -If a class derives from TObject and contains a TArrayF
  rootcint generates the Streamer code correctly.
 -If a class derives from TObject and contains a pointer to a TArrayF
  rootcint generates a Streamer class with the statement:
     R__b >> pointer;
  This statement must be changed to:
     pointer->Streamer(R__b);
  This could be done automatically by rootcint (not implemented).
 -If a class derives from TObject and contains a pointer to a basic type
  rootcint generates the code, issuing a warning: example
   In your class MuTrack, I have added a member
     Float_t *fX;
   You get the message:
    *** Datamember MuTrack::fX: pointer to fundamental type (need
manual    MuTrack::Streamer includes commented statements like:
      //R__b.WriteArray(fX, __COUNTER__);
   In such case, you typically have the array length as a data member
    (say member fN). Simply replace the commented statement by:
       R__b.WriteArray(fX,fN);

When you provide the function Streamer yourself, you can instruct
rootcint to not generate the code for Streamer by adding the character
"-" to the class name in the file LinkDef.h. Example

      #pragma link C++ class MuTrack-;

We are currently discussing:
  - the possibility to derive the TArray classes from TObject
  - the possibility to generate automatically the code in case
    of a pointer to a basic type. Consider the following example
       Int_t     fN;
       Float_t  *fX;  //[fN] array of floats with length fN
    If rootcint finds a reference to a data member in the comments,
    it could automatically generate the instruction:
       R__b.WriteArray(fX,fN);

Now concerning your last point with TVector, your code is correct.
You hit a problem in the function TVector::ResizeTo.
This problem has now been fixed in our development version.
Thanks for reporting it.

Rene Brun


Sergey Avvakumov wrote:
> 
> Hi everybody,
> 
> I have to say from the start, that I'm quite new to c++ and ROOT, so I'm
> probably asking about some trivial things. I'm trying to use ROOT to
> replace some ZEBRA data banks and the information about an event consists
> of few integers and floats and a variable length array of floats.  So I
> wrote a small program to accomplish this, trying to follow closely
> examples 'Event.h, Event.cxx, MainEvent.cxx' from $ROOTSYS/test directory.
> First I wanted to make a pointer to TArrayF to be a data member of my
> class "MyTrack", then I read in documentation, that for classes, not
> derived from TObject (and TArray is not) 'Streamer' method is not
> generated automatically, so it's not possible to write them into a file.
> Then I thought I can just copy 'Event.h', having a pointer to TClonesArray
> a data member and just replacing 'Tracks' with 'object numbers', i.e.
> floats, derived from TObject, but there is no such thing in ROOT by
> default. I got my program working using 'TObjNum' class, like the one from
> 'tcollex.cxx', but because there's no such 'built-in' class, I thought
> that there should be a better way to manage an array of floats. My last
> try was to use a pointer to TVector as a data member, because it is an
> array of floats, derived from TObject, this is how it looks:
> 
> MuTrack.h:
> 
> #ifndef __MuTrack__
> #define __MuTrack__
> 
> //////////////////////////////////////////////////////////////////////////
> //                                                                      //
> // MuTrack                                                              //
> //                                                                      //
> // Class definition for storing muon track pulseheights                 //
> //                                                                      //
> //////////////////////////////////////////////////////////////////////////
> 
> #include "TObject.h"
> #include "TVector.h"
> 
> class MuTrack : public TObject {
> 
> private:
>   Int_t          fLength;         // Track length
>   Int_t          fStart;          // Most upstream counter of the track
>   Int_t          fShwrLength;     // Shower length of the event, muon track
>                                   // taken from
>   Float_t        fShwrEnergy;     // Shower energy
>   Float_t        fMuEnergy;       // Muon energy
>   TVector       *fMuPulseHeights; // Vector of muon pulseheights
> 
>   static TVector* fgMuPulseHeights;
> 
> public:
>   MuTrack();      // Default constructor
>   virtual ~MuTrack();     // Destructor
> 
>   void     SetLength(Int_t n) { fLength = n; }
>   void     SetStart(Int_t n) { fStart = n; }
>   void     SetShwrLength(Int_t n) { fShwrLength = n; }
>   void     SetShwrEnergy(Float_t e) { fShwrEnergy = e; }
>   void     SetMuEnergy(Float_t e) { fMuEnergy = e; }
> 
>   void     ResizeTo(Int_t n) { fMuPulseHeights->ResizeTo(n); }
>   void     Copy(TVector& v) { fMuPulseHeights->operator=(v); }
>   TVector* GetMuPulseHeights() const { return fMuPulseHeights; }
> 
>   ClassDef(MuTrack,1)  // Class definition macro
> };
> 
> #endif
> 
> ------------------------------------------------------------------------
> 
> MuTrack.cxx:
> 
> #include "MuTrack.h"
> #include <iostream.h>
> 
> ClassImp(MuTrack)
> 
> TVector* MuTrack::fgMuPulseHeights = 0;
> 
> //___________________________________________________________________________
> 
> MuTrack::MuTrack()
> {
>   // Create a MuTrack object.
>   // When the constructor is invoked for the first time, the class static
>   // variable fgMuPulseHeights is 0 and the TVector fgMuPulseHeights
>   // is created.
> 
>   if (!fgMuPulseHeights) fgMuPulseHeights = new TVector(84);
>   fMuPulseHeights = fgMuPulseHeights;
>   fLength = 0;
>   fStart = 0;
>   fShwrLength = 0;
>   fShwrEnergy = 0.0;
>   fMuEnergy = 0.0;
> }
> 
> MuTrack::~MuTrack () { }
> 
> ---------------------------------------------------------------------------
> 
> MainMuTrack.cxx :
> 
> #include <stdlib.h>
> #include <iostream.h>
> 
> #include "TROOT.h"
> #include "TFile.h"
> #include "TRandom.h"
> #include "TTree.h"
> #include "TBranch.h"
> #include "TVector.h"
> 
> #include "MuTrack.h"
> 
> //____________________________________________________________________________
> int main()
> {
>   TROOT simple("simple","Example of creation of a tree");
> 
>   Int_t nevent = 400;     // by default create 400 events
>   Int_t comp   = 1;       // by default file is compressed
>   Int_t split  = 1;       // by default, split Event in sub branches
> 
>   TFile* hfile;
>   MuTrack* mutrack;
>   TTree* tree;
> 
>   Int_t nb = 0;
>   Int_t ev;
>   Int_t bufsize;
> 
>   // Create a new ROOT binary machine independent file.
>   // Note that this file may contain any kind of ROOT objects, histograms,
>   // pictures, graphics objects, detector geometries, tracks, events, etc..
>   // This file is now becoming the current directory.
>   hfile = new TFile("Event.root","RECREATE","TTree benchmark ROOT file");
>   hfile->SetCompressionLevel(comp);
> 
>   // Create one event
>   mutrack = new MuTrack();
> 
>   // Create a ROOT Tree and one superbranch
>   tree = new TTree("T","An example of a ROOT tree");
>   tree->SetAutoSave(1000000);  // autosave when 1 Mbyte written
>   bufsize = 256000;
>   if (split)  bufsize /= 4;
>   tree->Branch("event", "MuTrack", &mutrack, bufsize,split);
> 
>   // Write out 'nevent' random events.
>   for (ev = 0; ev < nevent; ev++)
>     {
>       cout << "Event " << ev << endl;
>       Int_t length = (Int_t)(50*gRandom->Rndm(1)+1);
>       Int_t start = (Int_t)(80*gRandom->Rndm(1)+1);
>       Int_t shwrlength = (Int_t)(10*gRandom->Rndm(1)+1);
>       Float_t shwrenergy = gRandom->Rndm(1)*200.0;
>       Float_t muenergy = gRandom->Rndm(1)*90.0;
> 
>       mutrack->SetLength(length);
>       mutrack->SetStart(start);
>       mutrack->SetShwrLength(shwrlength);
>       mutrack->SetShwrEnergy(shwrenergy);
>       mutrack->SetMuEnergy(muenergy);
> 
>       TVector* vector = new TVector(length);
>       for (Int_t i = 0; i < length; i++)
>         {
>           Float_t ph = gRandom->Rndm(1)*90.0;
>           vector->operator()(i) = ph;
>         }
> 
>       mutrack->ResizeTo(length);
>       mutrack->Copy(*vector);
>       delete vector;
>       nb += tree->Fill();  //fill the tree
>     }
> 
>   hfile->Write();
>   tree->Print();
> 
>   hfile->Close();
>   return 0;
> }
> ---------------------------------------------------------------------------
> 
> This program works, but occasionally I it prints out error messages:
> 
> CustomReAlloc2: oldsize != size
> 
> I sort of figured out that these errors are from resizing vector
> *fMuPulseHeights, the memory checker reports an error despite,
> that MuTrack() constructor 'reserves' enough memory, with
> fgMuPulseHeights = new TVector(84), to hold the largest
> possible vector. What I don't understand is why this error
> is not printed every time the size of *fMuPulseHeights gets
> increased, but only occasionally.
> 
> So my questions are - first, how to fix this program, eliminating
> these memory errors, and second what is the 'best' way to have an
> array of floats as a data member of the class, so that it's 'easy' to
> write objects of this class into a root file?
> 
> Thank you very much, especially to those, who read the entire posting.
> 
> Sergei Avvakumov
> avva@fnal.gov



This archive was generated by hypermail 2b29 : Tue Jan 04 2000 - 00:34:39 MET