// @(#)root/cont:$Name: $:$Id: TRef.cxx,v 1.29 2005/08/26 12:17:36 brun Exp $
// Author: Rene Brun 28/09/2001
/*************************************************************************
* Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/
//////////////////////////////////////////////////////////////////////////
//
// TRef
//
// Persistent Reference link to a TObject
// A TRef is a lightweight object pointing to any TObject.
// This object can be used instead of normal C++ pointers in case
// - the referenced object R and the pointer P are not written to the same file
// - P is read before R
// - R and P are written to different Tree branches
//
// When a top level object (eg Event *event) is a tree/graph of many objects,
// the normal ROOT Streaming mechanism ensures that only one copy of each object
// in the tree/graph is written to the output buffer to avoid circular
// dependencies.
// However if the object event is split into several files or into several
// branches of one or more Trees, normal C++ pointers cannot be used because
// each I/O operation will write the referenced objects.
// When a TRef is used to point to a TObject *robj.
// For example in a class with
// TRef fRef;
// one can do:
// fRef = robj; //to set the pointer
// this TRef and robj can be written with two different I/O calls
// in the same or different files, in the same or different branches of a Tree.
// If the TRef is read and the referenced object has not yet been read,
// the TRef will return a null pointer. As soon as the referenced object
// will be read, the TRef will point to it.
//
// TRef also supports the complex situation where a TFile is updated
// multiple times on the same machine or a different machine.
//
// How does it work
// ----------------
// A TRef is itself a TObject with an additional transient pointer fPID.
// When the statement fRef = robj is executed, the following actions happen:
// - The pointer fPID is set to the current TProcessID.
// - The current ObjectNumber (see below) is incremented by one.
// - robj::fUniqueID is set to ObjectNumber.
// - In the fPID object, the element fObjects[ObjectNumber] is set to robj
// - ref::fUniqueID is also set to ObjectNumber.
// After having set fRef, one can immediatly return the value of robj
// using fRef.GetObject(). This function returns directly fObjects[fUniqueID]
// from the fPID object.
//
// When the TRef is written, the process id number pidf of fPID is written
// in addition to the TObject part of TRef (fBits,fUniqueID).
// When the TRef is read, its pointer fPID is set to the value
// stored in the TObjArray of TFile::fProcessIDs (fProcessIDs[pidf]).
// The pidf is stored in the bits 24->31 of the fUniqueID of the TRef.
// This implies that the number of TRefs in one process should not
// exceed 2**23 = 8388608 and that the number of processes (different jobs)
// writing TRefs to one file should be less than 256.
// See section "ObjectNumber" below for a recipee to minimize the object count.
// If the objectnumber exceeds this limit, it could be the sign that:
// -The object count is never reset (see below)
// -TRef is misused.
//
// When a referenced object robj is written, TObject::Streamer writes
// in addition to the standard (fBits,fUniqueID) the pidf.
// When this robj is read by TObject::Streamer, the pidf is read.
// At this point, robj is entered into the table of objects of the TProcessID
// corresponding to pidf.
//
// WARNING1: If MyClass is the class of the referenced object, The TObject
// part of MyClass must be Streamed. One should not
// call MyClass::Class()->IgnoreTObjectStreamer()
//
//
// WARNING2: A TRef cannot point to another TRef.
//
//
// ObjectNumber
// ------------
// When an object is referenced (see TRef assignement operator or TRefArray::Add)
// a unique identifier is computed and stored in both the fUniqueID of the
// referenced and referencing object. This uniqueID is computed by incrementing
// by one the static global in TProcessID::fgNumber. fUniqueID is some sort of
// serial object number in the current session. One can retrieve at any time
// the current value of fgNumber by calling the static function TProcessID::GetObjectCount
// or set this number via TProcessID::SetObjectCount.
// To avoid a growing table of fObjects in TProcessID, in case, for example,
// one processes many events in a loop, it might be necessary to reset the
// ObjectNumber at the end of processing of one event. See an example
// in $ROOTSYS/test/Event.cxx (look at function Build).
// The value of ObjectNumber (say saveNumber=TProcessID::GetObjectCount()) may be
// saved at the beginning of one event and reset to this original value
// at the end of the event via TProcessID::SetObjectCount(saveNumber). These
// actions may be stacked.
//
// Action on Demand
// ----------------
// The normal behaviour of a TRef has been described above. In addition,
// TRef supports also "Actions on Demand". It may happen that the object
// referenced is not yet in memory, on a separate file or not yet computed.
// In this case TRef is able to automatically execute an action:
// - call to a compiled function (static function of member function)
// - call to an interpreted function
// - execution of a CINT script
//
// How to select this option?
// In the definition of the TRef data member in the original class, do:
// TRef fRef; //EXEC:execName. points to something
// When the special keyword "EXEC:" is found in the comment field of the member,
// the next string is assumed to be the name of a TExec object.
// When a file is connected, the dictionary of the classes on the file
// is read in memory (see TFile::ReadStreamerInfo). When the TStreamerElement
// object is read, a TExec object is automatically created with the name
// specified after the keywork "EXEC:" in case a TExec with a same name does
// not already exist.
// The action to be executed via this TExec can be specified with:
// - a call to the TExec constructor, if the constructor is called before
// opening the file.
// - a call to TExec::SetAction at any time.
// One can compute a pointer to an existing TExec with a name with:
// TExec *myExec = gROOT->GetExec(execName);
// myExec->SetAction(actionCommand); where
// - actionCommand is a string containing a CINT instruction. Examples:
// myExec->SetAction("LoadHits()");
// myExec->SetAction(".x script.C");
//
// When a TRef is dereferenced via TRef::GetObject, its TExec will be
// automatically executed. In the function/script being executed, one or more
// of the following actions can be executed:
// - load a file containing the referenced object. This function typically
// looks in the file catalog (GRID).
// - compute a pointer to the referenced object and communicate this pointer
// back to the calling function TRef::GetObject via:
// TRef::SetStaticObject(object).
// When the TExec is called, it has access to the dereferencing TRef
// by calling GetStaticObject() (TRef::GetObject() sets fgObject to "this"
// before the call to TExec). This can be useful for accessing the TRef's
// fUniqueID.
// As soon as an object is returned to GetObject, the fUniqueID of the TRef is set
// to the fUniqueID of the referenced object. At the next call to GetObject,
// the pointer stored in fPid:fObjects[fUniqueID] will be returned directly.
//
// An example of action on demand is shown in $ROOTSYS/test/Event.h with
// the member:
// TRef fWebHistogram; //EXEC:GetWebHistogram
// When calling fWebHistogram.GetObject(), the function GetObject
// will automatically invoke a script GetWebHistogram.C via the interpreter.
// An example of a GetWebHistogram.C script is shown below
// void GetWebHistogram() {
// TFile *f= TFile::Open("http://root.cern.ch/files/pippa.root");
// f->cd("DM/CJ");
// TH1 *h6 = (TH1*)gDirectory->Get("h6");
// h6->SetDirectory(0);
// delete f;
// TRef::SetStaticObject(h6);
// }
// In the above example, a call to fWebHistogram.GetObject() executes the
// script with the function GetWebHistogram. This script connects a file
// with histograms: pippa.root on the ROOT Web site and returns the object h6
// to TRef::GetObject.
// Note that if the definition of the TRef fWebHistogram had been:
// TRef fWebHistogram; //EXEC:GetWebHistogram()
// then, the compiled or interpreted function GetWebHistogram() would have
// been called instead of the CINT script GetWebHistogram.C
//
// Special case of a TRef pointing to an object with a TUUID
// ----------------------------------------------------------
// If the referenced object has a TUUID, its bit kHasUUID has been set.
// This case is detected by the TRef assignement operator.
// (For example, TFile and TDirectory have a TUUID)
// The TRef fPID points directly to the single object TProcessUUID (deriving
// from TProcessID) and managing the list of TUUIDs for a process.
// The TRef kHasUUID bit is set and its fUniqueID is set to the fUniqueID
// of the referenced object.
// When the TRef is streamed to a buffer, the corresponding TUUID is also
// streamed with the TRef. When a TRef is read from a buffer, the corresponding
// TUUID is also read and entered into the global list of TUUIDs (if not
// already there). The TRef fUniqueID is set to the UUIDNumber.
// see TProcessUUID for more details.
//
// Array of TRef
// -------------
// The special class TRefArray should be used to store multiple references.
// A TRefArray has one single pointer fPID for all objects in the array.
// It has a dynamic compact table of fUniqueIDs. Use a TRefArray rather
// then a collection of TRefs.
//
// Example:
// Suppose a TObjArray *mytracks containing a list of Track objects
// Suppose a TRefArray *pions containing pointers to the pion tracks in mytracks.
// This list is created with statements like: pions->Add(track);
// Suppose a TRefArray *muons containing pointers to the muon tracks in mytracks.
// The 3 arrays mytracks,pions and muons may be written separately.
//
//////////////////////////////////////////////////////////////////////////
#include "TRef.h"
#include "TROOT.h"
#include "TProcessUUID.h"
#include "TFile.h"
#include "TRefTable.h"
#include "TObjArray.h"
#include "TExec.h"
#include "TSystem.h"
#include "TObjString.h"
#include "TStreamerInfo.h"
#include "TStreamerElement.h"
TObjArray *TRef::fgExecs = 0;
TObject *TRef::fgObject = 0;
ClassImp(TRef)
//______________________________________________________________________________
TRef::TRef(TObject *obj)
{
// Create a ref to obj.
*this = obj;
}
//______________________________________________________________________________
TRef::TRef(const TRef &ref) : TObject(ref)
{
// TRef copy ctor.
*this = ref;
}
//______________________________________________________________________________
void TRef::operator=(TObject *obj)
{
// Assign object to reference.
UInt_t uid = 0;
fPID = 0;
if (obj) {
if (obj->IsA()->CanIgnoreTObjectStreamer()) {
Error("operator= ","Class: %s IgnoreTObjectStreamer. Cannot reference object",obj->ClassName());
return;
}
if (obj->TestBit(kHasUUID)) {
fPID = gROOT->GetUUIDs();
obj->SetBit(kIsReferenced);
SetBit(kHasUUID);
uid = obj->GetUniqueID();
} else {
if (obj->TestBit(kIsReferenced)) {
uid = obj->GetUniqueID();
fPID = TProcessID::GetProcessWithUID(uid,obj);
} else {
fPID = TProcessID::GetSessionProcessID();
uid = TProcessID::AssignID(obj);
}
ResetBit(kHasUUID);
}
}
SetUniqueID(uid);
}
//______________________________________________________________________________
TRef &TRef::operator=(const TRef &ref)
{
// TRef assignment operator.
SetUniqueID(ref.GetUniqueID());
fPID = ref.fPID;
SetBit(kHasUUID,ref.TestBit(kHasUUID));
return *this;
}
//______________________________________________________________________________
Bool_t operator==(const TRef &r1, const TRef &r2)
{
// Return kTRUE if r1 and r2 point to the same object.
if (r1.GetPID() == r2.GetPID() && r1.GetUniqueID() == r2.GetUniqueID()) return kTRUE;
else return kFALSE;
}
//______________________________________________________________________________
Bool_t operator!=(const TRef &r1, const TRef &r2)
{
// Return kTRUE if r1 and r2 do not point to the same object.
if (r1.GetPID() == r2.GetPID() && r1.GetUniqueID() == r2.GetUniqueID()) return kFALSE;
else return kTRUE;
}
//______________________________________________________________________________
Int_t TRef::AddExec(const char *name)
{
// If Exec with name does not exist in the list of Execs, it is created.
// returns the index of the Exec in the list.
if (!fgExecs) fgExecs = new TObjArray(10);
TExec *exec = (TExec*)fgExecs->FindObject(name);
if (!exec) {
//we register this Exec to the list of Execs.
exec = new TExec(name,"");
fgExecs->Add(exec);
}
return fgExecs->IndexOf(exec);
}
//______________________________________________________________________________
TObjArray *TRef::GetListOfExecs()
{
// Return a pointer to the static TObjArray holding the list of Execs.
if (!fgExecs) fgExecs = new TObjArray(10);
return fgExecs;
}
//______________________________________________________________________________
TObject *TRef::GetObject() const
{
// Return a pointer to the referenced object.
//TObject *obj = 0;
if (!fPID) return 0;
if (!TProcessID::IsValid(fPID)) return 0;
UInt_t uid = GetUniqueID();
//Try to find the object from the table of the corresponding PID
TObject *obj = fPID->GetObjectWithID(uid);
//the reference may be in the TRefTable
TRefTable *table = TRefTable::GetRefTable();
if (table) {
table->SetUID(uid);
table->Notify();
obj = fPID->GetObjectWithID(uid);
}
//if object not found, then exec action if an action has been defined
if (!obj) {
//execid in the first 8 bits
Int_t execid = TestBits(0xff0000);
if (execid > 0) {
execid = execid>>16;
TExec *exec = (TExec*)fgExecs->At(execid-1);
if (exec) {
//we expect the object to be returned via TRef::SetStaticObject
fgObject = const_cast<TRef*>(this);
exec->Exec();
if ((const TRef* const)fgObject!=this)
obj = fgObject;
else obj=0;
if (obj){
uid = TProcessID::AssignID(obj);
((TRef*)this)->SetUniqueID(uid);
fPID->PutObjectWithID(obj,uid);
} else {
//well may be the Exec has loaded the object
obj = fPID->GetObjectWithID(uid);
}
}
}
}
return obj;
}
//______________________________________________________________________________
void TRef::SetAction(const char *name)
{
// Store the exec number (in the ROOT list of Execs)
// into the fBits of this TRef.
TExec *exec = (TExec*)fgExecs->FindObject(name);
if (!exec) {
Error("SetAction","Unknow TExec: %s",name);
return;
}
Int_t execid = 1 + fgExecs->IndexOf(exec);
SetBit(execid << 8);
}
//______________________________________________________________________________
void TRef::SetAction(TObject *parent)
{
// Find the action to be executed in the dictionary of the parent class
// and store the corresponding exec number into fBits.
// This function searches a data member in the class of parent with an
// offset corresponding to this.
// If a comment "TEXEC:" is found in the comment field of the data member,
// the function stores the exec identifier of the exec statement
// following this keyword.
if (!parent) return;
Int_t offset = (char*)this - (char*)parent;
TClass *cl = parent->IsA();
cl->BuildRealData(parent);
TStreamerInfo *info = cl->GetStreamerInfo();
TIter next(info->GetElements());
TStreamerElement *element;
while((element = (TStreamerElement*)next())) {
if (element->GetOffset() != offset) continue;
Int_t execid = element->GetExecID();
if (execid > 0) SetBit(execid << 8);
return;
}
}
//______________________________________________________________________________
TObject *TRef::GetStaticObject() {
// Returns the static object.
return fgObject;
}
//______________________________________________________________________________
void TRef::SetObject(TObject *obj)
{
// static Obsolete function kept for back compatibility.
// In the near future will print a Warning, then will be deleted.
SetStaticObject(obj);
}
//______________________________________________________________________________
void TRef::SetStaticObject(TObject *obj)
{
// Static function to set the object found on the Action on Demand function.
// This function may be called by the user in the function called
// when a "EXEC:" keyword is specified in the data member field of the TRef.
// The function can get access to the dereferencing TRef (i.e. this)using
// the static function GetStaticObject().
fgObject = obj;
}
//______________________________________________________________________________
void TRef::Streamer(TBuffer &R__b)
{
// Stream an object of class TRef.
UShort_t pidf;
TFile *file = (TFile*)R__b.GetParent();
if (R__b.IsReading()) {
TObject::Streamer(R__b);
if (TestBit(kHasUUID)) {
TString s;
s.Streamer(R__b);
TProcessUUID *pid = gROOT->GetUUIDs();
UInt_t number = pid->AddUUID(s.Data());
fPID = pid;
SetUniqueID(number);
if (gDebug > 1) {
printf("Reading TRef (HasUUID) uid=%d, obj=%lx\n",GetUniqueID(),(Long_t)GetObject());
}
} else {
R__b >> pidf;
fPID = TProcessID::ReadProcessID(pidf,file);
//The execid has been saved in the unique id of the TStreamerElement
//being read by TStreamerElement::Streamer
//The current element (fgElement) is set as a static global
//by TStreamerInfo::ReadBuffer (Clones) when reading this TRef
Int_t execid = TStreamerInfo::GetCurrentElement()->GetUniqueID();
if (execid) SetBit(execid<<16);
if (gDebug > 1) {
printf("Reading TRef, pidf=%d, fPID=%lx, uid=%d, obj=%lx\n",pidf,(Long_t)fPID,GetUniqueID(),(Long_t)GetObject());
}
}
} else {
TObject::Streamer(R__b);
if (TestBit(kHasUUID)) {
TObjString *objs = gROOT->GetUUIDs()->FindUUID(GetUniqueID());
objs->String().Streamer(R__b);
if (gDebug > 1) {
printf("Writing TRef (HasUUID) uid=%d, obj=%lx\n",GetUniqueID(),(Long_t)GetObject());
}
} else {
pidf = TProcessID::WriteProcessID(fPID,file);
R__b << pidf;
if (gDebug > 1) {
printf("Writing TRef, pidf=%d, fPID=%lx, uid=%d, obj=%lx\n",pidf,(Long_t)fPID,GetUniqueID(),(Long_t)GetObject());
}
}
}
}
ROOT page - Class index - Class Hierarchy - Top of the page
This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.