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