TRef & TFile

Helo

I have problem with TRef.

I have two class A and B

class A : public Parent {
public:
A();
A(Int_t num);
~A();
Int_t GetNum() {return thenumA;}
ClassDef(A,1);
private:
Int_t thenumA;
}

class B : public Parent {
public:
B();
B(A *theA);
~B();
TRef referenceA; //->
Int_t thenumB;
ClassDef(B,1);
private:
};

/// Constructor
B::B(A *theA)
{
std::cout << “Creating Object of Class B” << std::endl;
thenumB = theA->GetNum();
referenceA = theA;
}

and when im save B class to TFile and read it back the reference in B is null and i dont know why.

WRITE
TFile file(“class.root”,“RECREATE”);
A *a = new A(10);
a->Write();
B *b = new B(a);
b->Write();

READ
TFile file(“class.root”,“READ”);
A *a = (A *)file.Get(“A;1”);
B *b = (B *)file.Get(“B;1”);
A *aget = (A *)(b->referenceA).GetObject();
if (aget) cout << aget->GetNum() << endl;
else cout << “aget is NULL !!!” << endl;

I cannot reproduce your problem.
See small tar file in teh download section.
Untar it, run the script go.
This produces a small shared lib AB.so
In an interactive session, do
root > .x test.C

You should get:
root [0] .x test.C
Creating Object of Class B
10

Rene
refs,tar.gz (0 Bytes)

I have had a simmilar problem, however I was able to discover that the reason the TRef objects return NULL is that the fUniqueID values in the referenced objects list of the TProcessID are different after the read. The value 0x01000000 is added to the fUniqueID values. Why would this be?
I have not been able to resolve this problem.

I have encountered the same problem as artur. I have also seen that the fUniqueID of the object has changed after the read. But I dont think that this is the only problem. I tried to change the fUniqueID back to the original value befor reading the reference, but that does not help.

Anyway, I have made a simple example, please tell me what is wrong with this code!

I first do :

  TRef rx;
  TNamed *x = new TNamed("x","");
  rx = x;

  TFile *f1 = new TFile("xy.root","RECREATE");
  x->Write();
  f1->Close();

  TFile *f2 = new TFile("ref.root","RECREATE");
  rx.Write();
  f2->Close();

Then :

  TFile *f4 = new TFile("xy.root","READ");
  TNamed *x2 = (TNamed*)f4->FindObjectAny("x");
  f4->Close();

  TFile *f3 = new TFile("ref.root","READ");
  TRef *rx2 = (TRef*)f3->FindObjectAny("TRef");
  f3->Close();

At this point rx2->GetObject() is NULL!

I’m using the windows version of root.

In your second session, you should not close the file containing the referenced object. Remove the line
f4->Close();

Rene

Hello,

I’m having a very similar problem Stanly had in April 2004. In my case
both objects are stored within the same file. And I don’t close the file
before attempting to get both (parent and its child) objecs. Here is what
I’m doing:

class Child : public TObject {

};

class Parent : public TObject {
public:
Parent() ; _child( new Child()) {}
TObject* child() const { return _child.GetObject( ); }

TRef _child;
};

TFile f( “MyFile.root” );
Parent* parent;
f.GetObject( “Parent;1”, parent );
TObject* child = aPtr->child( );
cout << child << endl;

What’s interesting is that when I’m reading an instance of “Parent” from
the file then a result for its “child” would depend on whether the above
shown reading sequence is being executed as a script from the “root” shell
or as from a standalone (compiled/linked) application. The “child” pointer
is valid only when the reading done from the “root” shell. When the
object is read from the standalone application then I’m always getting a
null pointer.

Any ideas why? Do I need any special initialization (tuneup) for my
standalone reader in order to make sure that the “child” is loaded
automatically when the “parent” is read?

By the way, I’m using 4.00-08 on Linux RH7.2, gcc-2.95.3.

Igor

Hi Igor,

In your standalone application, can you create a TApplication object?
see an example in $ROOTSYS/test/stress.cxx or hworld.cxx

Rene

Hi Rene,

adding this extra line doesn’t help. Here is what I have:

#include <iostream.h>

#include <TFile.h>
#include <TApplication.h>

#include “ParentChild.rdl”

int
main( int argc, char* argv[] )
{
TApplication theApp(“App”, &argc, argv);

TFile f( “MyFile.root” );
Parent* parent;
f.GetObject( “Parent;1”, parent );
TObject* child = aPtr->child( );
cout << child << endl;

Igor

Hi Igor,

Could you send me the shortest possible running test reproducing your problem?

Rene

Hi Rene,

here is the smallest possible setup I can imagine. It consists of 5 files:

A.rdl - defiitions for two persistent classes: A, B
ALinkDef.h - instructions for “rootcint” compiler
Writer.cc - a simplest writer for A (and B)
Reader.cc - a simplest reader illustrating the problem
HowToBuild - a sequence of actions to build the “project”

Here is the code:

// ======
// File: A.rdl
// ======

#ifndef A_RDL
#define A_RDL

#include “TObject.h”
#include “TRef.h”

class B : public TObject {
ClassDef(B,1) // A child class
};

class A : public TObject {
public:
A() : _b(new B()) {}
B* b( ) const { return (B*)(_b.GetObject( )); }
private:
TRef _b;
ClassDef(A,1) // A parent class
};
#endif

// ==========
// File: ALinkDef.h
// ==========

#ifdef CINT

#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;

#pragma link C++ class A+;
#pragma link C++ class B+;

#endif

// =========
// File: Writer.cc
// =========

#include “ADict.h”
#include <TFile.h>
#include <TApplication.h>

int
main( int argc, char* argv[] )
{
TApplication theApp(“App”, &argc, argv);
TFile f( “AData.root”, “recreate” );
A a;
f.WriteObject( &a, “A” );
f.Close( );
return 0;
}

// ==========
// File: Reader.cc
// ==========

#include <iostream.h>
#include <TFile.h>
#include <TApplication.h>
#include “ADict.h”

int
main( int argc, char* argv[] )
{
TApplication theApp(“App”, &argc, argv);
TFile f( “AData.root” );
A* a = 0;
f.GetObject( “A”, a );
B* b = a->b( );
cout << "a = " << a << ", b = " << b << “.” << endl;
return 0;
}

// ===========
// File: HowToBuild
// ===========

$ROOTSYS/bin/rootcint -f ADict.cc -c A.rdl ALinkDef.h
g++ -g -o Writer Writer.cc ADict.cc -I$ROOTSYS/include -L$ROOTSYS/lib -lCore -lCint
g++ -g -o Reader Reader.cc ADict.cc -I$ROOTSYS/include -L$ROOTSYS/lib -lCore -lCint

After you build the “project” run “Writer”. It will create a file called “AData.root”. You can see differences by trying to inspect this file either by the “Reader” or directly from “root” shell using the following script:

#include "A.rdl"
TFile f( “AData.root” );
A* a = 0;
f.GetObject( “A”, a );
B* b = a->b( );
cout << "a = " << a << ", b = " << b << “.” << endl;

Igor

Hi Igor,

You have several problems with your short example

  • your object B created by A was never written to the file.
    You must have an explicit reference to B in A (like shown in
    the example below). A TRef is not sufficient.

  • Do not put the creation of objects in your A default constructor.
    This will generate problems when reading.
    Use a different constructor.

// ======
// File: A.h
// ======

#ifndef A_RDL
#define A_RDL

#include “TObject.h”
#include “TRef.h”

class B : public TObject {
ClassDef(B,1) // A child class
};

class A : public TObject {
public:
A() : _bb(0) {}
A(B x) {_bb = x; _b = _bb;}
B
b( ) const { return (B*)(_b.GetObject( )); }

private:
B* _bb;
TRef _b;
ClassDef(A,1) // A parent class
};
ClassImp(A)
ClassImp(B)

#endif

// =========
// File: writer.C
// =========

#include “A.h”
#include <TFile.h>

int main( int argc, char* argv[] ) {
TFile f( “AData.root”, “recreate” );
A a(new B);
a.Write(“A” );
return 0;
}

// ==========
// File: reader.C
// ==========

#include <Riostream.h>
#include <TFile.h>
#include “A.h”

int main( int argc, char* argv[] ) {
TFile f( “AData.root” );
A* a = (A*)f.Get(“A”);
B* b = a->b( );
cout << "a = " << a << ", b = " << b << “.” << endl;
return 0;
}

rootcint -f ADict.C -c A.h ALinkDef.h
g++ -g -o writer writer.C ADict.C -I$ROOTSYS/include -L$ROOTSYS/lib -lCore -lCint
g++ -g -o reader reader.C ADict.C -I$ROOTSYS/include -L$ROOTSYS/lib -lCore -lCint

Rene

Hi Rene,

thanks for your reply! Now I’m beginning to understand a role of default constructors of persistent classes. My simplest test works after I fixed it according to your comments.

I have a few more question on the TRef class:

  1. Do I understand it correctly that (in my simple setup) a child object (of class “B”) would be automatically read from a file the first time I’m actually using the TRef pointing onto the child?

  2. Is it correct to say that before (1) above happens my parent object (of class “A”) would have a plain (C++) transient pointer onto a temporary transient object created using the default constructor of the child class “B”? That question is actually about my guess on the role of default constructors of persistent classes in ROOT I/O.

  3. Let’s assume that I’m going to have an array of TRef-s, using a special class TRefArray. Will all this array (the values of TRef-s, not the pointee objects) be read from a file the first time I use any of its elements? Will each of its pointee objects be read from a persistent store on demand, or they all be read at once?

A context of my third question is very simple. I may have, for example, a huge collection of persistent objects whose pointers are stored via TRefArray, and I don’t want to bring all this collection (table) of pointers into a virtual memory of my process just because I’m going to use 1 of these objects (out of millions). Here I’m also assuming that actual objects aren’t read all together (see my question #1 above).

In case if TRefArray doesn’t behave like I’d expect then I have my next question:

  1. Does ROOT I/O have an efficient collection of persistent pointers, which is brought into a virtual memory by used pages only (not all at once)? If not, then what would be the best way to model this in ROOT I/O? Shall I switch to some lower (“physical”) API of TFile?

    Thanks,
    Igor

Hi Igor,

May I suggest the following deal to you ::slight_smile:
We meet to discuss your very interesting mail, and you will post the result of our discussion. You have asked the key questions. It would be good if the answer
could be correctly documented somewhere.

Rene